From 4922d8755cc35564a4a304e54d5de379272b1cff Mon Sep 17 00:00:00 2001 From: builder_247 <14019974+builder-247@users.noreply.github.com> Date: Thu, 11 Jul 2024 15:43:23 +0300 Subject: [PATCH 1/7] swipable timetable --- components/timetable/DateSelector.tsx | 12 +---- elements/timetable/Timetable.tsx | 63 +++++++++++++-------------- package-lock.json | 10 +++++ package.json | 5 ++- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/components/timetable/DateSelector.tsx b/components/timetable/DateSelector.tsx index 75effd7..885edfc 100644 --- a/components/timetable/DateSelector.tsx +++ b/components/timetable/DateSelector.tsx @@ -10,19 +10,11 @@ dayjs.extend(weekday); interface DateSelectorProps { date: Date; - next: () => void; - previous: () => void; nextVisible?: boolean; previousVisible?: boolean; } -const DateSelector = ({ - date, - next, - previous, - nextVisible = true, - previousVisible = true, -}: DateSelectorProps) => { +const DateSelector = ({ date, nextVisible = true, previousVisible = true }: DateSelectorProps) => { const theme = useTheme(); const { i18n } = useTranslation(); @@ -52,7 +44,6 @@ const DateSelector = ({ top: 0, left: 6, }} - onPress={previous} /> )} @@ -68,7 +59,6 @@ const DateSelector = ({ top: 0, right: 6, }} - onPress={next} /> )} diff --git a/elements/timetable/Timetable.tsx b/elements/timetable/Timetable.tsx index 66c7b66..53114bc 100644 --- a/elements/timetable/Timetable.tsx +++ b/elements/timetable/Timetable.tsx @@ -3,23 +3,13 @@ import DateSelector from '@/components/timetable/DateSelector'; import EventsBox from '@/elements/timetable/EventsBox'; import { useEffect, useState } from 'react'; import { View } from 'react-native'; +import PagerView from 'react-native-pager-view'; import { ActivityIndicator } from 'react-native-paper'; const Timetable = () => { const [events, setEvents] = useState([]); - const [eventDayIndex, setEventDayIndex] = useState(0); - const previous = () => { - if (eventDayIndex > 0) { - setEventDayIndex(eventDayIndex - 1); - } - }; - - const next = () => { - if (eventDayIndex < events.length - 1) { - setEventDayIndex(eventDayIndex + 1); - } - }; + let currentDay = 0; useEffect(() => { getEvents().then((eventRes) => { @@ -46,33 +36,42 @@ const Timetable = () => { const index = eventsGroupedByDay.findIndex( (events) => events[events.length - 1].end.getTime() > new Date().getTime() ); // If last event of the day has ended, it's not the current day - setEventDayIndex(index === -1 ? eventsGroupedByDay.length - 1 : index); + currentDay = index === -1 ? eventsGroupedByDay.length - 1 : index; }); }, []); return ( - + {events.length === 0 ? ( ) : ( - <> - 0} - /> - - - - + + {events.map((day, index) => ( + + 0} + /> + + + + + ))} + )} ); diff --git a/package-lock.json b/package-lock.json index 0ebbc2b..a76160e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "react-i18next": "^14.1.2", "react-native": "0.74.2", "react-native-gesture-handler": "~2.16.1", + "react-native-pager-view": "^6.3.3", "react-native-paper": "^5.12.3", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "^4.10.1", @@ -15606,6 +15607,15 @@ "react": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-native-pager-view": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.3.3.tgz", + "integrity": "sha512-HViKBlfN/kBJUSu5mRL/V9Bkf1j7uDZozGAjbzh4o9XYo11qVcIK7IwvfzqrkNerVSDy/cAmZcXbcyWnII8xMA==", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-paper": { "version": "5.12.3", "license": "MIT", diff --git a/package.json b/package.json index 0b992c6..f7cce88 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@expo/vector-icons": "^14.0.0", + "@react-native-async-storage/async-storage": "1.23.1", "@react-navigation/native": "^6.0.2", "dayjs": "^1.11.11", "expo": "~51.0.14", @@ -35,12 +36,12 @@ "react-i18next": "^14.1.2", "react-native": "0.74.2", "react-native-gesture-handler": "~2.16.1", + "react-native-pager-view": "^6.3.3", "react-native-paper": "^5.12.3", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "^4.10.1", "react-native-screens": "3.31.1", - "react-native-web": "~0.19.10", - "@react-native-async-storage/async-storage": "1.23.1" + "react-native-web": "~0.19.10" }, "devDependencies": { "@babel/core": "^7.20.0", From d031dc2bcdaae433fa8a5fa7bd4bb8bea48bd257 Mon Sep 17 00:00:00 2001 From: builder_247 <14019974+builder-247@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:58:16 +0300 Subject: [PATCH 2/7] wip - static date selector --- elements/timetable/Timetable.tsx | 63 +++++++++++++++++++------------- hooks/usePagerPage.ts | 18 +++++++++ 2 files changed, 55 insertions(+), 26 deletions(-) create mode 100644 hooks/usePagerPage.ts diff --git a/elements/timetable/Timetable.tsx b/elements/timetable/Timetable.tsx index 53114bc..bd053e5 100644 --- a/elements/timetable/Timetable.tsx +++ b/elements/timetable/Timetable.tsx @@ -1,15 +1,26 @@ import { AssemblyEvent, getEvents } from '@/api/eventService'; import DateSelector from '@/components/timetable/DateSelector'; import EventsBox from '@/elements/timetable/EventsBox'; +import { usePagerPageHandler } from '@/hooks/usePagerPage'; import { useEffect, useState } from 'react'; import { View } from 'react-native'; import PagerView from 'react-native-pager-view'; import { ActivityIndicator } from 'react-native-paper'; +import Animated from 'react-native-reanimated'; + +const AnimatedPager = Animated.createAnimatedComponent(PagerView); const Timetable = () => { const [events, setEvents] = useState([]); + const [eventDayIndex, setEventDayIndex] = useState(0); - let currentDay = 0; + const onPageSelectedHandler = usePagerPageHandler({ + onPageSelected: (e: any) => { + 'worklet'; + console.log('Selected page ', e.position); + //setEventDayIndex(e.position); + }, + }); useEffect(() => { getEvents().then((eventRes) => { @@ -36,7 +47,7 @@ const Timetable = () => { const index = eventsGroupedByDay.findIndex( (events) => events[events.length - 1].end.getTime() > new Date().getTime() ); // If last event of the day has ended, it's not the current day - currentDay = index === -1 ? eventsGroupedByDay.length - 1 : index; + setEventDayIndex(index === -1 ? eventsGroupedByDay.length - 1 : index); }); }, []); @@ -45,33 +56,33 @@ const Timetable = () => { {events.length === 0 ? ( ) : ( - - {events.map((day, index) => ( - - 0} - /> - + 0} + /> + + {events.map((day, index) => ( + - - ))} - + ))} + + )} ); diff --git a/hooks/usePagerPage.ts b/hooks/usePagerPage.ts new file mode 100644 index 0000000..25c23d1 --- /dev/null +++ b/hooks/usePagerPage.ts @@ -0,0 +1,18 @@ +import { useEvent, useHandler } from 'react-native-reanimated'; + +export function usePagerPageHandler(handlers: any, dependencies?: any) { + const { context, doDependenciesDiffer } = useHandler(handlers, dependencies); + const subscribeForEvents = ['onPageSelected']; + + return useEvent( + (event) => { + 'worklet'; + const { onPageSelected } = handlers; + if (onPageSelected && event.eventName.endsWith('onPageSelected')) { + onPageSelected(event, context); + } + }, + subscribeForEvents, + doDependenciesDiffer + ); +} From c3493c24e0863640811fc4a119e5697ec2977065 Mon Sep 17 00:00:00 2001 From: builder_247 <14019974+builder-247@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:38:51 +0300 Subject: [PATCH 3/7] re-add previous and next buttons --- components/timetable/DateSelector.tsx | 12 ++++++- elements/timetable/Timetable.tsx | 24 ++++++------- hooks/useNavigationPanel.ts | 49 +++++++++++++++++++++++++++ hooks/usePagerPage.ts | 18 ---------- 4 files changed, 72 insertions(+), 31 deletions(-) create mode 100644 hooks/useNavigationPanel.ts delete mode 100644 hooks/usePagerPage.ts diff --git a/components/timetable/DateSelector.tsx b/components/timetable/DateSelector.tsx index 885edfc..75effd7 100644 --- a/components/timetable/DateSelector.tsx +++ b/components/timetable/DateSelector.tsx @@ -10,11 +10,19 @@ dayjs.extend(weekday); interface DateSelectorProps { date: Date; + next: () => void; + previous: () => void; nextVisible?: boolean; previousVisible?: boolean; } -const DateSelector = ({ date, nextVisible = true, previousVisible = true }: DateSelectorProps) => { +const DateSelector = ({ + date, + next, + previous, + nextVisible = true, + previousVisible = true, +}: DateSelectorProps) => { const theme = useTheme(); const { i18n } = useTranslation(); @@ -44,6 +52,7 @@ const DateSelector = ({ date, nextVisible = true, previousVisible = true }: Date top: 0, left: 6, }} + onPress={previous} /> )} @@ -59,6 +68,7 @@ const DateSelector = ({ date, nextVisible = true, previousVisible = true }: Date top: 0, right: 6, }} + onPress={next} /> )} diff --git a/elements/timetable/Timetable.tsx b/elements/timetable/Timetable.tsx index bd053e5..e78e5fc 100644 --- a/elements/timetable/Timetable.tsx +++ b/elements/timetable/Timetable.tsx @@ -1,8 +1,8 @@ import { AssemblyEvent, getEvents } from '@/api/eventService'; import DateSelector from '@/components/timetable/DateSelector'; import EventsBox from '@/elements/timetable/EventsBox'; -import { usePagerPageHandler } from '@/hooks/usePagerPage'; -import { useEffect, useState } from 'react'; +import { useNavigationPanel } from '@/hooks/useNavigationPanel'; +import { useCallback, useEffect, useState } from 'react'; import { View } from 'react-native'; import PagerView from 'react-native-pager-view'; import { ActivityIndicator } from 'react-native-paper'; @@ -14,13 +14,10 @@ const Timetable = () => { const [events, setEvents] = useState([]); const [eventDayIndex, setEventDayIndex] = useState(0); - const onPageSelectedHandler = usePagerPageHandler({ - onPageSelected: (e: any) => { - 'worklet'; - console.log('Selected page ', e.position); - //setEventDayIndex(e.position); - }, - }); + const callback = useCallback((position: number) => { + console.log('Hey', `You are on ${position + 1} page`); + }, []); + const { ref, nextPage, previousPage, ...navigationPanel } = useNavigationPanel(callback); useEffect(() => { getEvents().then((eventRes) => { @@ -47,7 +44,7 @@ const Timetable = () => { const index = eventsGroupedByDay.findIndex( (events) => events[events.length - 1].end.getTime() > new Date().getTime() ); // If last event of the day has ended, it's not the current day - setEventDayIndex(index === -1 ? eventsGroupedByDay.length - 1 : index); + // setEventDayIndex(index === -1 ? eventsGroupedByDay.length - 1 : index); }); }, []); @@ -67,11 +64,14 @@ const Timetable = () => { date={events[eventDayIndex][0].start} nextVisible={eventDayIndex < events.length - 1} previousVisible={eventDayIndex > 0} + next={nextPage} + previous={previousPage} /> ; + +export function useNavigationPanel(onPageSelectedCallback: (position: number) => void = () => {}) { + const ref = useRef(null); + const [activePage, setActivePage] = useState(0); + const [isAnimated, setIsAnimated] = useState(true); + const onPageSelectedPosition = useRef(new Animated.Value(0)).current; + + const setPage = useCallback( + (page: number) => + isAnimated ? ref.current?.setPage(page) : ref.current?.setPageWithoutAnimation(page), + [isAnimated] + ); + + const nextPage = () => setPage(activePage + 1); + const previousPage = () => setPage(activePage - 1); + + const onPageSelected = useMemo( + () => + Animated.event( + [{ nativeEvent: { position: onPageSelectedPosition } }], + { + listener: ({ nativeEvent: { position } }) => { + setActivePage(position); + onPageSelectedCallback(position); + }, + useNativeDriver: true, + } + ), + [] + ); + + return { + ref, + activePage, + nextPage, + previousPage, + isAnimated, + setPage, + onPageSelected, + }; +} diff --git a/hooks/usePagerPage.ts b/hooks/usePagerPage.ts deleted file mode 100644 index 25c23d1..0000000 --- a/hooks/usePagerPage.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useEvent, useHandler } from 'react-native-reanimated'; - -export function usePagerPageHandler(handlers: any, dependencies?: any) { - const { context, doDependenciesDiffer } = useHandler(handlers, dependencies); - const subscribeForEvents = ['onPageSelected']; - - return useEvent( - (event) => { - 'worklet'; - const { onPageSelected } = handlers; - if (onPageSelected && event.eventName.endsWith('onPageSelected')) { - onPageSelected(event, context); - } - }, - subscribeForEvents, - doDependenciesDiffer - ); -} From 5557400e724dd3ff0ef98fd75adcf667171d01b9 Mon Sep 17 00:00:00 2001 From: builder_247 <14019974+builder-247@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:12:12 +0300 Subject: [PATCH 4/7] fix --- elements/timetable/Timetable.tsx | 8 ++++---- hooks/useNavigationPanel.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/elements/timetable/Timetable.tsx b/elements/timetable/Timetable.tsx index e78e5fc..97a0927 100644 --- a/elements/timetable/Timetable.tsx +++ b/elements/timetable/Timetable.tsx @@ -15,9 +15,9 @@ const Timetable = () => { const [eventDayIndex, setEventDayIndex] = useState(0); const callback = useCallback((position: number) => { - console.log('Hey', `You are on ${position + 1} page`); + setEventDayIndex(position); }, []); - const { ref, nextPage, previousPage, ...navigationPanel } = useNavigationPanel(callback); + const { ref, nextPage, previousPage, onPageSelected } = useNavigationPanel(callback); useEffect(() => { getEvents().then((eventRes) => { @@ -44,7 +44,7 @@ const Timetable = () => { const index = eventsGroupedByDay.findIndex( (events) => events[events.length - 1].end.getTime() > new Date().getTime() ); // If last event of the day has ended, it's not the current day - // setEventDayIndex(index === -1 ? eventsGroupedByDay.length - 1 : index); + setEventDayIndex(index === -1 ? eventsGroupedByDay.length - 1 : index); }); }, []); @@ -71,7 +71,7 @@ const Timetable = () => { ref={ref} useNext={false} initialPage={eventDayIndex} - {...navigationPanel} + onPageSelected={onPageSelected} layoutDirection='ltr' orientation='horizontal' style={{ flex: 1 }} diff --git a/hooks/useNavigationPanel.ts b/hooks/useNavigationPanel.ts index e6a9044..55610c0 100644 --- a/hooks/useNavigationPanel.ts +++ b/hooks/useNavigationPanel.ts @@ -31,7 +31,7 @@ export function useNavigationPanel(onPageSelectedCallback: (position: number) => setActivePage(position); onPageSelectedCallback(position); }, - useNativeDriver: true, + useNativeDriver: false, } ), [] From 41b453acc8c6a352f34d99c459f27121f7fca0ae Mon Sep 17 00:00:00 2001 From: Samu Kupiainen Date: Thu, 18 Jul 2024 18:15:59 +0300 Subject: [PATCH 5/7] fix: event favorites read as bulk operation --- components/timetable/Event.tsx | 7 ++++--- elements/timetable/EventsBox.tsx | 10 +++++++++- elements/timetable/Timetable.tsx | 4 +++- hooks/useFavorite.ts | 29 ++++++++++++----------------- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/components/timetable/Event.tsx b/components/timetable/Event.tsx index 67eeebb..fa8ed81 100644 --- a/components/timetable/Event.tsx +++ b/components/timetable/Event.tsx @@ -12,6 +12,8 @@ interface EventProps { end: Date; color: string; thumbnail: string; + isFavorite: boolean; + toggleFavorite: () => void; } const getEventTimeString = (start: Date, end: Date) => { @@ -25,9 +27,8 @@ const getEventTimeString = (start: Date, end: Date) => { return `${startTime.format('HH:mm')} - ${endTime.format('HH:mm')}`; }; -const Event = ({ id, title, location, start, end, color, thumbnail }: EventProps) => { +const Event = ({ id, title, location, start, end, color, thumbnail, isFavorite, toggleFavorite }: EventProps) => { const timeString = getEventTimeString(start, end); - const { favorite, toggle: toggleFavorite } = useFavorite(id); const { t, i18n } = useTranslation(); dayjs.locale(i18n.language); @@ -105,7 +106,7 @@ const Event = ({ id, title, location, start, end, color, thumbnail }: EventProps process.env.EXPO_PUBLIC_ENVIRONMENT === 'preview') && ( toggleFavorite()} - icon={favorite ? 'heart' : 'heart-outline'} + icon={isFavorite ? 'heart' : 'heart-outline'} style={{ position: 'absolute', top: 0, diff --git a/elements/timetable/EventsBox.tsx b/elements/timetable/EventsBox.tsx index 77d0f8d..c19b4d0 100644 --- a/elements/timetable/EventsBox.tsx +++ b/elements/timetable/EventsBox.tsx @@ -1,12 +1,18 @@ import { AssemblyEvent } from '@/api/eventService'; import Event from '@/components/timetable/Event'; import { ScrollView } from 'react-native'; +import { useFavorite } from '@/hooks/useFavorite'; interface EventsBoxProps { events: AssemblyEvent[]; + favorites: number[]; + toggleFavorite: (id: number) => void; } -const EventsBox = ({ events }: EventsBoxProps) => { +const EventsBox = ({ events, favorites, toggleFavorite +}: EventsBoxProps) => { + + return ( { end={event.end} color={event.color} thumbnail={event.thumbnail} + toggleFavorite={() => toggleFavorite(event.id)} + isFavorite={favorites.includes(event.id)} /> ))} diff --git a/elements/timetable/Timetable.tsx b/elements/timetable/Timetable.tsx index 97a0927..37f2204 100644 --- a/elements/timetable/Timetable.tsx +++ b/elements/timetable/Timetable.tsx @@ -1,6 +1,7 @@ import { AssemblyEvent, getEvents } from '@/api/eventService'; import DateSelector from '@/components/timetable/DateSelector'; import EventsBox from '@/elements/timetable/EventsBox'; +import { useFavorite } from '@/hooks/useFavorite'; import { useNavigationPanel } from '@/hooks/useNavigationPanel'; import { useCallback, useEffect, useState } from 'react'; import { View } from 'react-native'; @@ -13,6 +14,7 @@ const AnimatedPager = Animated.createAnimatedComponent(PagerView); const Timetable = () => { const [events, setEvents] = useState([]); const [eventDayIndex, setEventDayIndex] = useState(0); + const { favorites, toggle: toggleFavorite } = useFavorite(); const callback = useCallback((position: number) => { setEventDayIndex(position); @@ -78,7 +80,7 @@ const Timetable = () => { > {events.map((day, index) => ( - + ))} diff --git a/hooks/useFavorite.ts b/hooks/useFavorite.ts index c514584..e7be4db 100644 --- a/hooks/useFavorite.ts +++ b/hooks/useFavorite.ts @@ -33,31 +33,26 @@ const saveFavorites = async (favorites: number[]) => { /** * Hook to manage favorite state for a given eventId. * - * @param id - The ID of the event to track favorite state for. - * @returns An object containing the current favorite state and a function to toggle the favorite state. + * @returns An object containing the current favorites and a function to toggle the favorite state by id. */ -export const useFavorite = (id: number) => { - const [favorite, setFavorite] = useState(false); +export const useFavorite = () => { + const [favorites, setFavorites] = useState([]); useEffect(() => { getFavorites().then((favorites) => { - setFavorite(favorites.includes(id)); + setFavorites(favorites); }); - }, [id]); + }, []); - const toggle = async () => { - let favorites = await getFavorites(); - - if (favorite) { - favorites.push(id); + const toggle = async (id: number) => { + if (!favorites.includes(id)) { + setFavorites([...favorites, id]); + await saveFavorites([...favorites, id]); } else { - favorites.filter((f) => f !== id); + setFavorites(favorites.filter((n) => n !== id)); + await saveFavorites(favorites.filter((n) => n !== id)); } - - saveFavorites(favorites); - // Trigger redraw - setFavorite(!favorite); }; - return { favorite, toggle }; + return { favorites, toggle }; }; From d27a3c4c2e8edeb9811536ba8354ec5fa918de4c Mon Sep 17 00:00:00 2001 From: Samu Kupiainen Date: Thu, 18 Jul 2024 18:28:48 +0300 Subject: [PATCH 6/7] fix: linting issues --- components/timetable/Event.tsx | 1 - hooks/useNavigationPanel.ts | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/timetable/Event.tsx b/components/timetable/Event.tsx index fa8ed81..c231bd1 100644 --- a/components/timetable/Event.tsx +++ b/components/timetable/Event.tsx @@ -1,4 +1,3 @@ -import { useFavorite } from '@/hooks/useFavorite'; import dayjs from 'dayjs'; import { useTranslation } from 'react-i18next'; import { Image, View } from 'react-native'; diff --git a/hooks/useNavigationPanel.ts b/hooks/useNavigationPanel.ts index 55610c0..240ea6a 100644 --- a/hooks/useNavigationPanel.ts +++ b/hooks/useNavigationPanel.ts @@ -34,7 +34,7 @@ export function useNavigationPanel(onPageSelectedCallback: (position: number) => useNativeDriver: false, } ), - [] + [onPageSelectedCallback, onPageSelectedPosition] ); return { @@ -43,6 +43,7 @@ export function useNavigationPanel(onPageSelectedCallback: (position: number) => nextPage, previousPage, isAnimated, + setIsAnimated, setPage, onPageSelected, }; From b9e4d8db46a561a38791e12a3ad83744b04ecbd8 Mon Sep 17 00:00:00 2001 From: AntoKeinanen Date: Thu, 18 Jul 2024 18:30:33 +0300 Subject: [PATCH 7/7] chore: format code --- components/timetable/Event.tsx | 12 +++++++++++- elements/timetable/EventsBox.tsx | 6 +----- elements/timetable/Timetable.tsx | 6 +++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/components/timetable/Event.tsx b/components/timetable/Event.tsx index c231bd1..0a03ca0 100644 --- a/components/timetable/Event.tsx +++ b/components/timetable/Event.tsx @@ -26,7 +26,17 @@ const getEventTimeString = (start: Date, end: Date) => { return `${startTime.format('HH:mm')} - ${endTime.format('HH:mm')}`; }; -const Event = ({ id, title, location, start, end, color, thumbnail, isFavorite, toggleFavorite }: EventProps) => { +const Event = ({ + id, + title, + location, + start, + end, + color, + thumbnail, + isFavorite, + toggleFavorite, +}: EventProps) => { const timeString = getEventTimeString(start, end); const { t, i18n } = useTranslation(); dayjs.locale(i18n.language); diff --git a/elements/timetable/EventsBox.tsx b/elements/timetable/EventsBox.tsx index c19b4d0..5d632ae 100644 --- a/elements/timetable/EventsBox.tsx +++ b/elements/timetable/EventsBox.tsx @@ -1,7 +1,6 @@ import { AssemblyEvent } from '@/api/eventService'; import Event from '@/components/timetable/Event'; import { ScrollView } from 'react-native'; -import { useFavorite } from '@/hooks/useFavorite'; interface EventsBoxProps { events: AssemblyEvent[]; @@ -9,10 +8,7 @@ interface EventsBoxProps { toggleFavorite: (id: number) => void; } -const EventsBox = ({ events, favorites, toggleFavorite -}: EventsBoxProps) => { - - +const EventsBox = ({ events, favorites, toggleFavorite }: EventsBoxProps) => { return ( { > {events.map((day, index) => ( - + ))}