Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / components / hooks / useSubscribedCalendars.ts
blob931b77612f4abc4407df91f046c2cb5087fa7406
1 import { useEffect, useMemo, useState } from 'react';
3 import useEventManager from '@proton/components/hooks/useEventManager';
4 import { useLoading } from '@proton/hooks';
5 import { getSubscriptionParameters } from '@proton/shared/lib/api/calendars';
6 import { getIsSubscribedCalendar, getVisualCalendars } from '@proton/shared/lib/calendar/calendar';
7 import { getIsCalendarSubscriptionEventManagerUpdate } from '@proton/shared/lib/calendar/subscribe/helpers';
8 import {
9     findMemberIndices,
10     getIsCalendarEventManagerCreate,
11     getIsCalendarEventManagerDelete,
12     getIsCalendarEventManagerUpdate,
13     getIsCalendarMemberEventManagerCreate,
14     getIsCalendarMemberEventManagerDelete,
15     getIsCalendarMemberEventManagerUpdate,
16 } from '@proton/shared/lib/eventManager/calendar/helpers';
17 import type {
18     Calendar,
19     CalendarMember,
20     CalendarSubscription,
21     CalendarSubscriptionResponse,
22     CalendarWithOwnMembers,
23     SubscribedCalendar,
24     VisualCalendar,
25 } from '@proton/shared/lib/interfaces/calendar';
26 import type {
27     CalendarEventManager,
28     CalendarMemberEventManager,
29     CalendarSubscriptionEventManager,
30 } from '@proton/shared/lib/interfaces/calendar/EventManager';
31 import addItem from '@proton/utils/addItem';
32 import removeItem from '@proton/utils/removeIndex';
33 import updateItem from '@proton/utils/updateItem';
35 import { useCalendarModelEventManager } from '../containers/eventManager/calendar/CalendarModelEventManagerProvider';
36 import useApi from './useApi';
38 const useSubscribedCalendars = (calendars: VisualCalendar[], loadingCalendars = false) => {
39     const [subscribedCalendars, setSubscribedCalendars] = useState<SubscribedCalendar[]>([]);
40     const [loading, withLoading] = useLoading(true);
41     const api = useApi();
43     const calendarIDs = useMemo(() => calendars.map(({ ID }) => ID), [calendars]);
45     const { subscribe: coreSubscribe } = useEventManager();
46     const { subscribe: calendarSubscribe } = useCalendarModelEventManager();
48     const handleAddCalendar = async (calendar: CalendarWithOwnMembers) => {
49         if (!getIsSubscribedCalendar(calendar)) {
50             return;
51         }
52         const { CalendarSubscription } = await api<CalendarSubscriptionResponse>(
53             getSubscriptionParameters(calendar.ID)
54         );
56         setSubscribedCalendars((prevState = []) =>
57             getVisualCalendars([
58                 ...prevState,
59                 {
60                     ...calendar,
61                     SubscriptionParameters: CalendarSubscription,
62                 },
63             ])
64         );
65     };
67     const handleDeleteCalendar = (calendarID: string) => {
68         setSubscribedCalendars((subscribedCalendars) => {
69             const index = subscribedCalendars?.findIndex((calendar) => calendar.ID === calendarID);
71             return removeItem(subscribedCalendars, index);
72         });
73     };
75     const handleUpdateCalendar = (calendar: Calendar) => {
76         setSubscribedCalendars((subscribedCalendars) => {
77             const { ID } = calendar;
78             const index = subscribedCalendars?.findIndex((calendar) => calendar.ID === ID);
80             return updateItem(subscribedCalendars, index, {
81                 ...subscribedCalendars[index],
82                 ...calendar,
83             });
84         });
85     };
87     const handleDeleteMember = (memberID: string) => {
88         setSubscribedCalendars((subscribedCalendars) => {
89             const [calendarIndex, memberIndex] = findMemberIndices(memberID, subscribedCalendars);
90             if (calendarIndex === -1 || memberIndex === -1) {
91                 return subscribedCalendars;
92             }
93             const oldCalendar = subscribedCalendars[calendarIndex];
95             return updateItem(subscribedCalendars, calendarIndex, {
96                 ...oldCalendar,
97                 Members: removeItem(oldCalendar.Members, memberIndex),
98             });
99         });
100     };
102     const handleCreateMember = (member: CalendarMember) => {
103         setSubscribedCalendars((subscribedCalendars) => {
104             const { ID: memberID, CalendarID } = member;
105             const [calendarIndex, memberIndex] = findMemberIndices(memberID, subscribedCalendars, CalendarID);
106             if (calendarIndex === -1 || memberIndex !== -1) {
107                 return subscribedCalendars;
108             }
109             const oldCalendar = subscribedCalendars[calendarIndex];
111             return updateItem(subscribedCalendars, calendarIndex, {
112                 ...oldCalendar,
113                 Members: addItem(oldCalendar.Members, member),
114             });
115         });
116     };
118     const handleUpdateMember = (member: CalendarMember) => {
119         setSubscribedCalendars((subscribedCalendars) => {
120             const { ID: memberID, CalendarID } = member;
121             const [calendarIndex, memberIndex] = findMemberIndices(memberID, subscribedCalendars, CalendarID);
122             if (calendarIndex === -1 || memberIndex === -1) {
123                 return subscribedCalendars;
124             }
125             const oldCalendar = subscribedCalendars[calendarIndex];
127             return getVisualCalendars(
128                 updateItem(subscribedCalendars, calendarIndex, {
129                     ...oldCalendar,
130                     Members: updateItem(oldCalendar.Members, memberIndex, member),
131                 })
132             );
133         });
134     };
136     const handleUpdateSubscription = (calendarID: string, subscription: CalendarSubscription) => {
137         setSubscribedCalendars((subscribedCalendars) => {
138             const index = subscribedCalendars?.findIndex((calendar) => calendar.ID === calendarID);
139             const oldCalendar = subscribedCalendars[index];
140             return updateItem(subscribedCalendars, index, {
141                 ...oldCalendar,
142                 SubscriptionParameters: {
143                     ...oldCalendar.SubscriptionParameters,
144                     ...subscription,
145                 },
146             });
147         });
148     };
150     useEffect(() => {
151         if (loadingCalendars) {
152             return;
153         }
155         return coreSubscribe(
156             ({
157                 Calendars: CalendarEvents = [],
158                 CalendarMembers: CalendarMembersEvents = [],
159             }: {
160                 Calendars?: CalendarEventManager[];
161                 CalendarMembers?: CalendarMemberEventManager[];
162             }) => {
163                 CalendarEvents.forEach((event) => {
164                     if (getIsCalendarEventManagerDelete(event)) {
165                         handleDeleteCalendar(event.ID);
166                     }
168                     if (getIsCalendarEventManagerCreate(event)) {
169                         // TODO: The code below is prone to race conditions. Namely if a new event manager update
170                         //  comes before this promise is resolved.
171                         void handleAddCalendar(event.Calendar);
172                     }
174                     if (getIsCalendarEventManagerUpdate(event)) {
175                         // TODO: The code below is prone to race conditions. Namely if a new event manager update
176                         //  comes before this promise is resolved.
177                         void handleUpdateCalendar(event.Calendar);
178                     }
179                 });
181                 CalendarMembersEvents.forEach((event) => {
182                     if (getIsCalendarMemberEventManagerDelete(event)) {
183                         handleDeleteMember(event.ID);
184                     } else if (getIsCalendarMemberEventManagerCreate(event)) {
185                         handleCreateMember(event.Member);
186                     } else if (getIsCalendarMemberEventManagerUpdate(event)) {
187                         handleUpdateMember(event.Member);
188                     }
189                 });
190             }
191         );
192     }, [loadingCalendars]);
194     useEffect(() => {
195         if (loadingCalendars) {
196             return;
197         }
199         return calendarSubscribe(
200             calendarIDs,
201             ({
202                 CalendarSubscriptions: CalendarSubscriptionEvents = [],
203             }: {
204                 CalendarSubscriptions?: CalendarSubscriptionEventManager[];
205             }) => {
206                 CalendarSubscriptionEvents.forEach((calendarSubscriptionChange) => {
207                     if (getIsCalendarSubscriptionEventManagerUpdate(calendarSubscriptionChange)) {
208                         const { ID, CalendarSubscription } = calendarSubscriptionChange;
209                         handleUpdateSubscription(ID, CalendarSubscription);
210                     }
211                 });
212             }
213         );
214     }, [calendarIDs, loadingCalendars]);
216     useEffect(() => {
217         const run = async () => {
218             const newSubscribedCalendars = await Promise.all(
219                 calendars.map(async (calendar) => {
220                     const { CalendarSubscription } = await api<CalendarSubscriptionResponse>(
221                         getSubscriptionParameters(calendar.ID)
222                     );
224                     return {
225                         ...calendar,
226                         SubscriptionParameters: CalendarSubscription,
227                     };
228                 })
229             );
231             void setSubscribedCalendars(newSubscribedCalendars);
232         };
234         if (loadingCalendars) {
235             return;
236         }
238         void withLoading(run());
239     }, [loadingCalendars]);
241     // Refresh subscribed calendars when the list of calendars changes (example: visibility change)
242     useEffect(() => {
243         if (loadingCalendars || loading) {
244             return;
245         }
247         setSubscribedCalendars((prevState) => {
248             return prevState.map((calendar) => {
249                 const visualCalendar = calendars.find(({ ID }) => ID === calendar.ID);
250                 if (!visualCalendar) {
251                     return calendar;
252                 }
253                 return {
254                     ...calendar,
255                     ...visualCalendar,
256                 };
257             });
258         });
259     }, [calendars, loadingCalendars, loading]);
261     return { subscribedCalendars, loading };
264 export default useSubscribedCalendars;