Merge branch 'translations_2024-12-05_050340' into 'main'
[ProtonMail-WebClient.git] / packages / shared / lib / calendar / recurrence / rruleSubset.ts
blob992d16330158e6529e5969ea6432e14fbc3db46b
1 import { convertZonedDateTimeToUTC, fromUTCDate, toUTCDate } from '../../date/timezone';
2 import type { VcalVeventComponent } from '../../interfaces/calendar';
3 import { propertyToUTCDate } from '../vcalConverter';
4 import { getPropertyTzid } from '../vcalHelper';
5 import { getIsAllDay } from '../veventHelper';
6 import type { RecurringResult } from './recurring';
7 import { getOccurrences, getOccurrencesBetween } from './recurring';
8 import { getIsRruleEqual } from './rruleEqual';
10 export const getAreOccurrencesSubset = (
11     newOccurrences: (RecurringResult | Pick<RecurringResult, 'localStart'>)[],
12     oldVevent: VcalVeventComponent
13 ) => {
14     const cache = {};
15     for (const { localStart } of newOccurrences) {
16         const isAllDay = getIsAllDay(oldVevent);
17         const startTzid = getPropertyTzid(oldVevent.dtstart);
18         let utcStart = localStart;
19         if (!isAllDay && startTzid) {
20             utcStart = toUTCDate(convertZonedDateTimeToUTC(fromUTCDate(localStart), startTzid));
21         }
22         const [oldOccurrence] = getOccurrencesBetween(oldVevent, +utcStart, +utcStart, cache);
23         if (!oldOccurrence) {
24             return false;
25         }
26     }
27     return true;
30 /**
31  * Return true if the set of occurrences of the new rrule is contained (equality counts) in the old rrule.
32  * Return false otherwise
33  * We restrict to rrules that can be created by us
34  */
35 export const getIsRruleSubset = (newVevent: VcalVeventComponent, oldVevent: VcalVeventComponent) => {
36     const [{ rrule: newRrule, dtstart: newDtstart }, { rrule: oldRrule }] = [newVevent, oldVevent];
37     const isRruleEqual = getIsRruleEqual(newRrule, oldRrule);
38     if (!newRrule || !oldRrule || isRruleEqual) {
39         return isRruleEqual;
40     }
41     const { count: oldCount, until: oldUntil } = oldRrule.value;
42     const { count: newCount, until: newUntil } = newRrule.value;
43     if (!newCount && !newUntil && (oldCount || oldUntil)) {
44         return false;
45     }
46     /**
47      *  Given the repeating nature of recurring rules, we're gonna play dirty here
48      *  and simply check if the at max 10 first occurrences of the new recurring rule are generated by the old one.
49      *  For the recurring rules we support this is more than enough, but it wouldn't be
50      *  in a more general (and pathological) scenario.
51      *  The alternative would be to write some code checking the frequency, interval,
52      *  byday, etc properties and going case by case. That code would be cumbersome and ugly
53      */
54     // either newUntil or newCount are not undefined, so we can check if all new occurrences are in the original set
55     // but for performance we use the same trick as above and check max 10
56     const maxCount = newCount ? Math.min(newCount, 10) : 10;
57     const newOccurrences = newUntil
58         ? getOccurrencesBetween(newVevent, +propertyToUTCDate(newDtstart), +toUTCDate(newUntil)).slice(0, 10)
59         : getOccurrences({ component: newVevent, maxCount });
61     return getAreOccurrencesSubset(newOccurrences, oldVevent);