Commit Graph
11 Commits
Author SHA1 Message Date
Linus Rath 41a0716c50 Merge branch 'main' into feat/stalwart-1.0 2026-10-16 01:41:48 +02:00
Linus Rath a4b5ed9c8f fix: keep the organizer and list occurrences once in the rest of a split series
Two faults in the new series "This and following events" makes, found
on 0.16.23 (1.0 does not show them):

- The new series named its organizer only when the editor wrote one,
  which it does only for an event that had none. Created without it,
  0.16.23 makes the user who creates the event its organizer, whatever
  the owner participant says: a sharee who split the owner's meeting
  became the organizer of the rest (live: the sharee's address,
  isOrigin false), and a series organized under an alias would have
  moved to the user's main address. 1.0 takes the owner participant.
  The new series now carries the series' organizer address whenever it
  has participants.
- The overrides the new series takes over kept the old series'
  SEQUENCE while the new series starts at its own, and 0.16.23 listed
  each such occurrence twice, as changed and as the series has it
  (live). The overrides now leave the old number behind.
2026-09-26 04:47:03 +02:00
Linus Rath 27f57864cf fix: keep a sharee's own answer and raise sequences from the server's copy
Two leftovers of 9f7c12ab and 021a6732:

- A sharee who attends the owner's meeting and moves it in the owner's
  calendar was asked again along with everyone else. Stalwart 0.16
  counts the addresses of the user who writes as the account's own and
  sends them nothing, so her answer stayed needs-action in the owner's
  copy with nothing asking her to answer again, and her own copy kept
  the old time (live on 0.16.23; 1.0 sends her the change). A move, a
  drag or a new rule now keeps the answer of the user who makes it, in
  the series and in the overrides it rewrites; the other attendees are
  asked again as before. The rest of a split series still asks
  everyone, the user too: 1.0 invites to a new event only who has not
  answered.
- The SEQUENCE a change of every occurrence raises came from the series
  as the app held it. A series opened from a link while another client
  changed it gave a lower number than the attendees had, which 0.16
  stores and the attendees refuse, and a pointer into an override
  removed meanwhile failed the whole save. The store now reads the
  series right before that write, through the account the write goes
  to, and the raise starts from what the server holds: the series'
  SEQUENCE, the overrides it has now, and for an override the change
  rewrites at least one above the series. 1.0 raises the numbers the
  same way itself, so the result there is unchanged (live).
2026-09-26 04:45:25 +02:00
Linus Rath 977a55f229 fix: keep occurrence changes on their days, and drop them when repeating stops
Two faults in how a series' changed and cancelled occurrences follow a
change of every occurrence, both from e6b8c4ed (live on 1.0 and
0.16.23, organizer and attendee copies):

- A rule that names its days - Mondays and Tuesdays, weekdays, the 5th
  of the month - keeps its occurrences on those days when the series'
  start moves. Moving every occurrence by a day moved each override
  along anyway, so the change of Monday 14 December landed on the key
  of Tuesday 15 December and one silently overwrote the other: the
  cancelled Tuesday came back with Monday's title, and Monday lost its
  change. Under such a rule every override but the first occurrence's
  now stays on its day, at the series' new time, or is dropped when the
  series no longer has that occurrence; the time 0.16 keeps in an
  override stays with it. The rest of a series split "this and
  following" keeps them the same way. Two overrides that still meet on
  one occurrence - the first occurrence's, moved onto the Tuesday after
  it when the series starts a day later - no longer overwrite each
  other: the one that stays where it is, or on its day, keeps the
  occurrence, and the other is left out and logged.
- "Repeat: none" for every occurrence kept the override of the first
  occurrence on the single event that was left, because a series'
  start counts as an occurrence of any rule. Cancelled, the event was
  gone from every range on 1.0 (still stored); retitled, 0.16.23 listed
  it twice. A series made single keeps no overrides, as before
  e6b8c4ed.
2026-09-26 04:43:45 +02:00
Linus Rath 021a67327b fix: let attendees on 0.16 take a series change after one occurrence changed
Stalwart 0.16 (0.16.6 to 0.16.23) sends a change of one occurrence of a
meeting with that occurrence's SEQUENCE raised, but does not keep the
raise itself: in its organizer update code the list of components to
raise is shadowed for changes of single occurrences. A later change of
every occurrence that does not raise the number again - a new title,
another description - then went out with the old number for that
occurrence, and the attendee's server refused the whole message as out
of sequence. The attendee got the "Updated invitation" mail, but their
calendar stayed as it was (live on 0.16.23; 1.0 raises and keeps the
number itself).

A change of every occurrence that the user as organizer sends to the
attendees, of something they see, now raises the SEQUENCE of the series
and of each changed occurrence by one, on a series that has changed
occurrences. One up from what the organizer's copy holds is at least
what the attendees got, and it keeps the series and its occurrences on
equal numbers, which 0.16 needs to list an occurrence once. 1.0 raises
the number of every occurrence the same way for a change it sends, and
keeps a number it would have given: the result there is the same
(live).
2026-09-26 02:49:49 +02:00
Linus Rath f7d1a86b5e fix: keep series intact when a split fails and when edited from one occurrence
Edits of a recurring meeting from one of its occurrences went wrong in
several ways (live on 1.0 and 0.16.23, attendees included):

- A "this and following" split whose new series could not be created
  stayed half done: the calendar store answers most failures with null,
  and only a thrown error put the old series back. The series stayed
  ended, told so to the attendees, and the save said it had worked. A
  null now puts the series back as well and fails the save. The rollback
  writes the old overrides back as a whole map: a pointer into a series
  without overrides fails, and the split may just have removed its only
  one.
- "This occurrence" on the series itself (a meeting opened from a link
  or a search result) went out in one write that reached no attendee
  who had answered. It now takes the same two steps as an occurrence of
  an expanded series: the override as it is without messages, then the
  change of it.
- The new series of a split started at the opened occurrence's own time
  and in the user's zone: Mondays in New York, one occurrence moved to
  Tuesday, renamed from there by a user in Berlin, became a Tuesday
  series in Berlin. It now goes on from that occurrence's recurrence id,
  in the series' zone, moved only by what the editor moved; the
  occurrence keeps its own time through its override. It takes the old
  series' details with what the user changed, not the opened
  occurrence's own ones.
- "All events" from an occurrence with an override of its own wrote that
  occurrence's own title, copied into its override, back to the series:
  a new room there renamed the series. Only the fields the user changed
  go to the series now; the opened occurrence's override takes them too
  (on 1.0 also where it holds them as pointers), and other overrides
  where they only held the series' old value.
- The move of an "all" edit was measured on the user's clock: across a
  week in which only one of the two zones changes to or from summer
  time, the series moved an hour more or less than picked. It is now
  measured on the series' own clock.
- 0.16 (0.16.23 checked) reads a rule's UNTIL as the series' wall clock
  taken for UTC, so in a zone behind UTC a split or "delete this and
  following" kept the occurrence it was to end before. The UNTIL is now
  earlier by the zone's offset, which ends the series there on 0.16 and
  1.0 alike.
- An applied counter proposal for one occurrence of a recurring meeting
  went to the first event with its uid. It now goes to the occurrence
  the proposal names; one for a whole series of which only occurrences
  are in view is not applied to any of them.
2026-09-26 02:48:11 +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 a6e6a5e201 fix: show a new series' occurrences and keep single edits off the series
A live Lite run on 1.0 and 0.16.23 found two faults with a series just
created:

- Its later occurrences did not appear until the page was reloaded. The
  store added the series itself to the grid and did not load the range
  again, and only the range load expands a series (on the server, or in
  the browser). The store now reloads the visible range after creating
  a series, and after any change to a series or one that makes an event
  a series or a single event again.
- That one item was the series itself, and "This event only" on it
  wrote the editor's data to the series: `CalendarEvent/set` on the
  series' id with its title, start and rule, so every occurrence
  changed. "Delete this event only" deleted the series. The series
  itself now stands for its first occurrence: a change becomes an
  override of that occurrence, and a delete excludes it. "This and
  following events" from the first occurrence changes, or deletes, the
  whole series instead of ending it before its first occurrence. No
  single-occurrence path falls back to the series' own id any more; only
  an occurrence the server keeps as an event of its own (an invitation
  to that occurrence alone) is changed or deleted as itself, and a
  series that cannot be found is refused with the usual error.

The delete scopes moved from calendar-app.tsx to
`lib/recurrence-scope-edit.ts`, next to the edit scopes, so they are
tested.
2026-09-26 00:27:20 +02:00
Linus Rath 6d4b695489 fix: ask attendees again when their organizer drags a meeting
A move outside the editor kept every answer. A drag or resize in the
week and day views, a drop in the month view and an applied counter
proposal wrote the new time with the attendees' stored answers:
Stalwart's update then said "accepted" for a time nobody had accepted,
and a dragged occurrence, which gets its own override, reached no
attendee who had answered (live on 1.0 and 0.16.23). When the user
organizes the meeting these moves now ask the attendees again, as the
editor does; one helper (`userOrganizes`) decides for both.

Also:
- A changed recurrence rule counts as a move for the series: a custom
  rule moved from Monday to Tuesday asks again. A rule the user did not
  touch is no change however the server spells it, and on one
  occurrence, where no rule applies, it is none.
- An event that lasts no time (a deadline) keeps its zero length on
  save instead of becoming an hour long and asking everyone again.
- An attendee's save of their own copy - an alert on one occurrence, a
  drag - wrote the participants again and sent the organizer a
  "Reply:" mail. It now leaves the participants alone and sends
  nothing. The editor shows someone else's attendee list read-only and
  no "Send invitations" box, which only ever controlled the organizer's
  messages; answers go out through the answer buttons.
- An existing override written on the base event (0.16 before 0.16.20,
  and ranges the browser expands) gets the series' organizer again when
  it has none, as every save wrote it before 4c20b09c: 0.16.19 refuses
  an answer to such an occurrence. 0.16.23 takes the key and drops it;
  1.0 refuses it inside an override once, and the store writes the
  override again without it (both live).
- The comment on `organizerCalendarAddress` said Stalwart takes the
  ORGANIZER solely from it. Both versions take it from the owner
  participant when it is missing (live).
2026-09-26 00:25:27 +02:00
Linus Rath 75b432e957 fix: reach attendees who answered with series splits and first edits
Stalwart sends a new event, and an occurrence that gets its own override
for the first time, only to attendees who have not answered (PARTSTAT
absent or NEEDS-ACTION); a change to an existing event or override goes
to everyone who has not declined. Since the editor keeps the attendees'
answers on a save (4c20b09c), two edits never reached the attendees who
had accepted (live on 1.0 and 0.16.23):

- "This and following events" made the rest of the series a new event
  with the answers the old one had, so those attendees got nothing, and
  it ended the old series without scheduling messages, so they kept
  every occurrence of it. The new series now asks every attendee again,
  and the old one is shortened with scheduling messages when the user
  organizes it and sends invitations; a failed create puts it back the
  same way. Deleting "this and following events" ends the series the
  same way.
- The first change to one occurrence that keeps the answers - a new
  title, another room - was sent only to attendees who had not
  answered. When the user organizes the event, sends invitations and
  an attendee has answered, the store now gives the occurrence its
  override first, as it is and without messages, finds it again
  (0.16.20 renumbers synthetic ids once an override exists) and then
  writes the change with messages. The attendee gets the update and
  keeps their answer. This works through synthetic ids (0.16.20 on,
  1.0) and as an override on the base event (older 0.16, and ranges
  expanded in the browser).
2026-09-26 00:23:28 +02:00
Linus Rath e8ba9e3569 fix: keep a series' dates and answers when editing all occurrences
Saving "All events" from the editor opened on one occurrence wrote that
occurrence's data to the whole series. Renaming a weekly series from its
third occurrence moved the series' start to that day: the first two
occurrences were gone, a counted series ran two weeks longer, and
overrides before the new start were orphaned. The occurrence's
participants went to every occurrence as well, so an attendee who had
declined only that day now declined the series, and Stalwart sent them
no more updates (live on 1.0 and 0.16.23). The check for a move
compared the form with the occurrence, so the rename that moved the
series did not ask anyone again.

The series now keeps its own:
- when it happens, unless the occurrence was moved; then the series
  moves by as much, from its own start and in its own time zone, and
  the length changes only when the editor changed it
- its participants and their answers, with the attendees the user added
  or removed in the editor; when the series moves, its organizer asks
  the attendees again

The scope handling moved from calendar-app.tsx to
`lib/recurrence-scope-edit.ts` so it can be tested. The editor now hands
its attendees (as opened and as saved) and its dates to the save.
2026-09-26 00:21:57 +02:00