Commit Graph
13 Commits
Author SHA1 Message Date
Linus Rath c633185cda Merge branch 'main' into feat/stalwart-1.0
# Conflicts:
#	CONTRIBUTING.md
#	LICENSE
#	README.md
#	app/api/calendar-agenda/route.ts
#	app/api/wopi/files/[fileId]/contents/route.ts
#	app/api/wopi/launch/route.ts
#	components/calendar/calendar-app.tsx
#	components/calendar/calendar-day-view.tsx
#	components/calendar/calendar-month-view.tsx
#	components/calendar/calendar-week-view.tsx
#	components/calendar/event-modal.tsx
#	components/contacts/contact-detail.tsx
#	components/email/email-composer.tsx
#	components/email/email-list.tsx
#	components/files/file-browser.tsx
#	components/files/files-app.tsx
#	components/files/folder-tree-sidebar.tsx
#	components/filters/filter-rule-modal.tsx
#	components/mail/mail-app.tsx
#	hooks/use-mailbox-drop.ts
#	integration/run-tests.sh
#	lib/__tests__/calendar-agenda-route.test.ts
#	lib/__tests__/jmap-draft-lifecycle.test.ts
#	lib/__tests__/web-push.test.ts
#	lib/account-state-manager.ts
#	lib/jmap/client-interface.ts
#	lib/jmap/client.ts
#	lib/jmap/types.ts
#	lib/message-list-order.ts
#	lib/sieve/generator.ts
#	lib/sieve/parser.ts
#	lib/telemetry/payload.ts
#	lib/thread-utils.ts
#	lib/unified-mailbox.ts
#	lib/web-push.ts
#	lib/wopi/token.ts
#	locales/ar/common.json
#	locales/ca/common.json
#	locales/cs/common.json
#	locales/da/common.json
#	locales/de/common.json
#	locales/en/common.json
#	locales/es/common.json
#	locales/fa/common.json
#	locales/fr/common.json
#	locales/he/common.json
#	locales/hu/common.json
#	locales/it/common.json
#	locales/ja/common.json
#	locales/ko/common.json
#	locales/lv/common.json
#	locales/mn/common.json
#	locales/nb/common.json
#	locales/nl/common.json
#	locales/pl/common.json
#	locales/pt/common.json
#	locales/ro/common.json
#	locales/ru/common.json
#	locales/sk/common.json
#	locales/tr/common.json
#	locales/uk/common.json
#	locales/zh-TW/common.json
#	locales/zh/common.json
#	scripts/lite/lib.mjs
#	stores/__tests__/calendar-store-synthetic-ids.test.ts
#	stores/__tests__/email-store-shared-folder-keywords.test.ts
#	stores/__tests__/email-store-unified-actions.test.ts
#	stores/__tests__/file-store.test.ts
#	stores/email-store.ts
#	stores/file-store.ts
#	stores/message-list-tabs-store.ts
2026-10-12 08:35:08 +02:00
Linus Rath 34fb9d247a fix: keep daily recurring events going past a DST gap
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.
2026-09-26 15:45:59 +02:00
Linus Rath e6b8c4ed50 fix: move a series' changed and cancelled occurrences along when it moves or splits
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.
2026-09-26 02:46:12 +02:00
Linus Rath 4c20b09c1c fix: show moved occurrences on 0.16.23 and keep organizers on save
- 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.
2026-09-25 20:20:00 +02:00
Linus Rath d0b0383660 fix: apply recurrence overrides as patches in browser expansion
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.
2026-09-25 17:42:59 +02:00
Linus Rath 28fd880582 fix: keep single-occurrence edits on that occurrence and keep its details 2026-09-23 13:40:01 +02:00
Linus Rath d800689924 feat: use Stalwart synthetic ids for recurring occurrences #140 2026-08-29 11:03:48 +02:00
Stefan Hildebrandt 0b7203df0f chore: clear pre-commit eslint warnings (unused symbols, stale disables, test any)
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.
2026-06-19 12:31:07 +02:00
Linus Rath be58cee989 fix: dedupe scheduling emails, Stalwart-compatible calendar filters 2026-06-11 18:24:43 +02:00
Linus Rath 8bdadc7ba3 fix: standardize punctuation 2026-04-16 19:07:42 +02:00
Linus Rath 9ee25c930e fix: compute per-occurrence utcStart/utcEnd in recurrence expansion #116 2026-04-02 13:35:26 +02:00
Linus Rath 1cce5c3c8a fix: JSCalendar 2.0 recurrenceRule single-object compatibility 116 2026-03-31 00:11:04 +02:00
Linus Rath 75b5d31414 feat: implement client-side recurrence expansion for calendar events 2026-03-29 17:12:30 +02:00