Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / applications / calendar / src / app / containers / alarms / useCalendarsAlarms.ts
blobd7aa33248ff806f4505fb23e8e8520ef73e64b3e
1 import type { MutableRefObject } from 'react';
2 import { useEffect, useMemo, useState } from 'react';
4 import { useApi } from '@proton/components';
5 import { DAY, MINUTE } from '@proton/shared/lib/constants';
6 import { addMilliseconds } from '@proton/shared/lib/date-fns-utc';
7 import type { Calendar as tsCalendar } from '@proton/shared/lib/interfaces/calendar';
8 import noop from '@proton/utils/noop';
10 import type { CalendarsAlarmsCache } from './CacheInterface';
11 import getCalendarsAlarmsCached from './getCalendarsAlarmsCached';
13 const PADDING = 2 * MINUTE;
15 export const getCalendarsAlarmsCache = ({
16     start = new Date(2000, 1, 1),
17     end = new Date(2000, 1, 1),
18 } = {}): CalendarsAlarmsCache => ({
19     calendarsCache: {},
20     start,
21     end,
22 });
24 const useCalendarsAlarms = (
25     calendars: tsCalendar[],
26     cacheRef: MutableRefObject<CalendarsAlarmsCache>,
27     lookAhead = 2 * DAY
28 ) => {
29     const api = useApi();
30     const [forceRefresh, setForceRefresh] = useState<any>();
32     const calendarIDs = useMemo(() => calendars.map(({ ID }) => ID), [calendars]);
34     useEffect(() => {
35         let timeoutHandle = 0;
36         let unmounted = false;
38         const update = async () => {
39             const now = new Date();
41             // Cache is invalid
42             if (+cacheRef.current.end - PADDING <= +now) {
43                 cacheRef.current = getCalendarsAlarmsCache({
44                     start: now,
45                     end: addMilliseconds(now, lookAhead),
46                 });
47                 cacheRef.current.rerender = () => setForceRefresh({});
48             }
50             const promise = getCalendarsAlarmsCached(api, cacheRef.current.calendarsCache, calendarIDs, [
51                 cacheRef.current.start,
52                 cacheRef.current.end,
53             ]);
54             cacheRef.current.promise = promise;
56             if (timeoutHandle) {
57                 clearTimeout(timeoutHandle);
58             }
60             await promise;
62             // If it's not the latest, ignore
63             if (unmounted || promise !== cacheRef.current.promise) {
64                 return;
65             }
67             const delay = Math.max(0, +cacheRef.current.end - PADDING - Date.now());
69             timeoutHandle = window.setTimeout(() => {
70                 update().catch(noop);
71             }, delay);
73             setForceRefresh({});
74         };
76         update().catch(noop);
78         cacheRef.current.rerender = () => setForceRefresh({});
79         return () => {
80             cacheRef.current.rerender = undefined;
81             unmounted = true;
82             clearTimeout(timeoutHandle);
83         };
84     }, [calendarIDs]);
86     return useMemo(() => {
87         const { calendarsCache } = cacheRef.current;
88         return calendarIDs
89             .map((calendarID) => {
90                 return calendarsCache[calendarID]?.result ?? [];
91             })
92             .flat()
93             .sort((a, b) => {
94                 return a.Occurrence - b.Occurrence;
95             });
96     }, [forceRefresh, calendarIDs]);
99 export default useCalendarsAlarms;