2 DEFAULT_FULL_DAY_NOTIFICATION,
3 DEFAULT_FULL_DAY_NOTIFICATIONS,
4 DEFAULT_PART_DAY_NOTIFICATION,
5 DEFAULT_PART_DAY_NOTIFICATIONS,
6 } from '@proton/shared/lib/calendar/alarms/notificationDefaults';
7 import { apiNotificationsToModel, notificationsToModel } from '@proton/shared/lib/calendar/alarms/notificationsToModel';
11 getIsSubscribedCalendar,
13 } from '@proton/shared/lib/calendar/calendar';
16 DEFAULT_EVENT_DURATION,
18 EVENT_VERIFICATION_STATUS,
24 } from '@proton/shared/lib/calendar/constants';
25 import { stripAllTags } from '@proton/shared/lib/calendar/sanitize';
26 import { getIsAllDay, getRecurrenceId } from '@proton/shared/lib/calendar/veventHelper';
27 import { fromLocalDate, toUTCDate } from '@proton/shared/lib/date/timezone';
28 import type { Address, RequireOnly, Address as tsAddress } from '@proton/shared/lib/interfaces';
38 CalendarSettings as tsCalendarSettings,
39 } from '@proton/shared/lib/interfaces/calendar';
40 import type { VcalVeventComponent } from '@proton/shared/lib/interfaces/calendar/VcalModel';
42 import type { SharedVcalVeventComponent } from '../../../containers/calendar/eventStore/interface';
43 import { getSnappedDate } from '../../calendar/mouseHelpers/dateHelpers';
44 import getFrequencyModelChange from './getFrequencyModelChange';
45 import { propertiesToModel } from './propertiesToModel';
46 import { propertiesToNotificationModel } from './propertiesToNotificationModel';
47 import { getDateTimeState } from './time';
49 export const getNotificationModels = ({
50 DefaultPartDayNotifications = DEFAULT_PART_DAY_NOTIFICATIONS,
51 DefaultFullDayNotifications = DEFAULT_FULL_DAY_NOTIFICATIONS,
54 defaultPartDayNotification: DEFAULT_PART_DAY_NOTIFICATION,
55 defaultFullDayNotification: DEFAULT_FULL_DAY_NOTIFICATION,
56 partDayNotifications: notificationsToModel(DefaultPartDayNotifications, false),
57 fullDayNotifications: notificationsToModel(DefaultFullDayNotifications, true),
61 export const getInitialDateTimeModel = (initialDate: Date, defaultEventDuration: number, tzid: string) => {
62 const snapInterval = 30;
64 const start = getSnappedDate(
67 initialDate.getUTCFullYear(),
68 initialDate.getUTCMonth(),
69 initialDate.getUTCDate(),
70 initialDate.getUTCHours(),
71 initialDate.getUTCMinutes() + snapInterval
79 start.getUTCFullYear(),
83 start.getUTCMinutes() + defaultEventDuration
88 start: getDateTimeState(start, tzid),
89 end: getDateTimeState(end, tzid),
93 export const getInitialFrequencyModel = (startDate: Date): FrequencyModel => {
96 frequency: FREQUENCY.WEEKLY,
98 daily: { type: DAILY_TYPE.ALL_DAYS },
99 weekly: { type: WEEKLY_TYPE.ON_DAYS, days: [startDate.getDay()] },
100 monthly: { type: MONTHLY_TYPE.ON_MONTH_DAY },
101 yearly: { type: YEARLY_TYPE.BY_MONTH_ON_MONTH_DAY },
102 ends: { type: END_TYPE.NEVER, count: 2 },
106 export const getInitialMemberModel = (
107 Addresses: tsAddress[],
108 Members: CalendarMember[],
109 Member: CalendarMember,
112 const { ID: addressID } = Address;
113 const { ID: memberID } = Member;
122 const getCalendarsModel = (Calendar: VisualCalendar, Calendars: VisualCalendar[] = []) => {
123 if (!Calendars.some(({ ID }) => ID === Calendar.ID)) {
124 throw new Error('Calendar not found');
127 calendars: Calendars.map((calendar) => ({
130 color: calendar.Color,
131 permissions: calendar.Permissions,
132 isSubscribed: getIsSubscribedCalendar(calendar),
133 isOwned: getIsOwnedCalendar(calendar),
134 isWritable: getIsCalendarWritable(calendar),
135 isUnknown: getIsUnknownCalendar(calendar),
139 color: Calendar.Color,
140 permissions: Calendar.Permissions,
141 isSubscribed: getIsSubscribedCalendar(Calendar),
142 isOwned: getIsOwnedCalendar(Calendar),
143 isWritable: getIsCalendarWritable(Calendar),
144 isUnknown: getIsUnknownCalendar(Calendar),
149 export const getOrganizerAndSelfAddressModel = ({
155 attendees: AttendeeModel[];
157 addresses: Address[];
160 if (!attendees.length || isAttendee) {
164 const organizerAddress = addresses.find(({ ID }) => ID === addressID);
166 if (!organizerAddress) {
171 organizer: { email: organizerAddress.Email, cn: organizerAddress.DisplayName },
172 selfAddress: organizerAddress,
176 interface GetInitialModelArguments {
178 CalendarSettings: tsCalendarSettings;
179 Calendar: VisualCalendar;
180 Calendars: VisualCalendar[];
181 Members: CalendarMember[];
182 Member: CalendarMember;
183 Addresses: tsAddress[];
186 verificationStatus?: EVENT_VERIFICATION_STATUS;
188 attendees?: AttendeeModel[];
191 export const getInitialModel = ({
192 initialDate = toUTCDate(fromLocalDate(new Date())), // Needs to be in fake utc time
201 verificationStatus = EVENT_VERIFICATION_STATUS.NOT_VERIFIED,
204 }: GetInitialModelArguments): EventModel => {
205 const { DefaultEventDuration: defaultEventDuration = DEFAULT_EVENT_DURATION } = CalendarSettings;
206 const dateTimeModel = getInitialDateTimeModel(initialDate, defaultEventDuration, tzid);
207 const frequencyModel = getInitialFrequencyModel(dateTimeModel.start.date);
208 const notificationModel = getNotificationModels(CalendarSettings);
209 const memberModel = getInitialMemberModel(Addresses, Members, Member, Address);
210 const calendarsModel = getCalendarsModel(Calendar, Calendars);
211 const organizerModel = getOrganizerAndSelfAddressModel({
213 addressID: memberModel.member.addressID,
214 addresses: Addresses,
229 isOrganizer: !!attendees.length,
231 isProtonProtonInvite: false,
232 hasDefaultNotifications: true,
233 status: ICAL_EVENT_STATUS.CONFIRMED,
234 defaultEventDuration,
236 hasTouchedRrule: false,
237 ...notificationModel,
238 hasPartDayDefaultNotifications: true,
239 hasFullDayDefaultNotifications: true,
246 const getParentMerge = ({
247 veventComponentParentPartial,
249 hasDefaultNotifications,
250 isProtonProtonInvite,
253 veventComponentParentPartial: SharedVcalVeventComponent;
254 recurrenceStart: DateTimeModel;
255 hasDefaultNotifications: boolean;
256 isProtonProtonInvite: boolean;
259 const isAllDay = getIsAllDay(veventComponentParentPartial);
260 const parentModel = propertiesToModel({
261 veventComponent: veventComponentParentPartial,
263 hasDefaultNotifications,
264 isProtonProtonInvite,
267 const { frequencyModel, start } = parentModel;
269 frequencyModel: getFrequencyModelChange(start, recurrenceStart, frequencyModel),
273 interface GetExistingEventArguments {
274 veventComponent: VcalVeventComponent;
275 hasDefaultNotifications: boolean;
276 veventComponentParentPartial?: SharedVcalVeventComponent;
277 isProtonProtonInvite: boolean;
279 selfAddressData: SelfAddressData;
280 calendarSettings: CalendarSettings;
284 export const getExistingEvent = ({
286 hasDefaultNotifications,
287 veventComponentParentPartial,
288 isProtonProtonInvite,
292 }: GetExistingEventArguments): RequireOnly<EventModel, 'isAllDay' | 'description'> => {
293 const isAllDay = getIsAllDay(veventComponent);
294 const recurrenceId = getRecurrenceId(veventComponent);
296 const newModel = propertiesToModel({
298 hasDefaultNotifications,
301 isProtonProtonInvite,
304 const strippedDescription = stripAllTags(newModel.description);
306 const notifications = hasDefaultNotifications
307 ? apiNotificationsToModel({ notifications: null, isAllDay, calendarSettings })
308 : propertiesToNotificationModel(veventComponent, isAllDay);
311 veventComponentParentPartial && recurrenceId
313 veventComponentParentPartial,
314 recurrenceStart: newModel.start,
315 hasDefaultNotifications,
316 isProtonProtonInvite,
323 description: strippedDescription,
328 fullDayNotifications: notifications,
331 partDayNotifications: notifications,