Daily expansion advanced its period anchor day by day and kept the
time a DST gap pushed it to: 02:30 became 03:30 on the spring-forward
night, and an all-day event's midnight became 01:00 where the change
happens at midnight (Chile, Brazil). Every later day then failed the
implicit byHour, so the series vanished from that night on.
Each daily candidate is now built from the start's wall-clock time on
its day and matched against that time, so the gap day still shows its
occurrence (moved past the gap) and the days after it are back at the
normal time.
A series keeps its changed and cancelled occurrences as overrides keyed
by recurrence id, where the rule puts each occurrence. Two edits left
them behind (live on 1.0 and 0.16.23, attendees included):
- Moving every occurrence kept the overrides under the old ids. A weekly
series moved an hour got its cancelled 16 December back, at 11:00,
and showed its retitled 30 December twice, at 10:00 and at 11:00;
1.0 doubled it in the attendee's copy as well.
- "This and following events" ended the rule but not the overrides after
the split, which then showed on as occurrences of the old series next
to the new one, and the new series got the whole COUNT again: six
weekly occurrences split at the third ran to eight dates, 4 January
twice. Deleting "this and following" left the later overrides too.
Now:
- Moving the series moves each override with its occurrence, in the same
update: by as much as the series' start moved, onto the same day at the
new time when the series turns all-day or timed. An occurrence moved on
its own keeps its time, except the one the editor was opened on, which
takes the move made there; an override that only held the time its
occurrence had moves along, and so does a pinned length. When the rule
changes, overrides of occurrences the new rule no longer has are
dropped. When the attendees are asked again, so they are in the
overrides that move.
- The split removes the overrides from the split on together with the
rule change and gives them to the new series, re-keyed and with the new
series' participants: 0.16 keeps an override as an event of its own,
so one without them reached no attendee. The new series' COUNT is what
is left of the old one; an UNTIL stays, and a rule the user changed in
the editor is taken as it is. A failed create puts the overrides back
with the rule.
- Delete "this and following" removes the overrides from there on.
- Stalwart 0.16.23 answers `utcStart` and `utcEnd` of a counted
series' master with "32548-12-04T15:30:07Z" once every occurrence is
overridden or excluded, and with the next plain occurrence's times
once the first one is; 1.0 answers the master's own. Expanding such
a series in the browser took the occurrences' length from those
instants and copied them onto the occurrences, so a moved occurrence
fell out of the range. The master's instants now count only where
they fit its wall clock, the length falls back to the JSCalendar
duration, the range check ignores instants that do not parse or fit,
and no occurrence gets the master's instants, so the alerts of an
all-day series expanded in the browser go off before each day, not
before the first.
- The event editor rebuilt the participants around the user's first
address on every save, set `organizerCalendarAddress` to it and put
every attendee back to needs-action. An invitation the user saved in
their own calendar became theirs and lost their answer, and one
occurrence of a series organized under another address or letter
case got a different owner; Stalwart 1.0 refuses a changed
`organizerCalendarAddress` there. The editor now keeps the event's
own organizer, writes `organizerCalendarAddress` only for an event
without one, and keeps the id and answer of every attendee who
stays.
Stalwart 1.0 returns recurrence overrides as RFC 8984 PatchObjects, with
keys like participants/p1/participationStatus and nulls that remove.
Browser expansion spread them onto the occurrence, so a declined
occurrence stayed accepted and the pointer keys became junk properties.
Moved or resized occurrences also kept the time and length the rule
gave them and were placed by their original date.
Overrides are now applied as patches, occurrence times come from the
patched start, time zone and duration, and new changes merge into an
existing override without keys that are prefixes of one another.
Cleans up the lint warnings the pre-commit hook surfaces, without any
behavioral change:
- Remove unused imports/vars/destructured props (parseISO, useEffect,
format, durMin, roles, daysInYear, ALLOWED_PLUGIN_FILES, continuesBefore,
isPushConnected, isSelected) and the now-unused parseDuration import.
- Drop three stale `// eslint-disable-next-line no-undef` directives that
no longer suppress anything (browser-navigation, smime/crypto-engine).
- recurrence-expansion.test.ts: replace 39 `as any` casts with a cast-only
`rule()` helper for partial recurrence-rule fixtures, typed access to
utcStart/utcEnd (now on CalendarEvent), and the source's
`Partial<CalendarEvent> & { excluded?: boolean }` for the excluded
override. No defaults are injected, so the expansion logic sees the same
partial rules as before (35 tests still green).
Remaining: 7 react-hooks/exhaustive-deps warnings are left as-is — adding
the missing deps changes effect/memo timing and needs per-hook review, not
a mechanical fix. tsc --noEmit clean; eslint 0 errors / 7 warnings.