Calendars were listed in the order the server returns them, which for
Stalwart is creation order, and the sortOrder property was never read.
Follow the JMAP Calendars rule instead: lower sortOrder first, equal
sortOrder by name (case- and accent-insensitive, numbers by value). The
store keeps the list sorted, so the sidebar, the calendar pickers and the
settings all show the same order; each account's calendars stay together.
Calendar settings gain move up / move down buttons, visible on touch
screens too, and drag and drop for the user's own calendars. A move
renumbers that account's calendars 1, 2, 3... and writes sortOrder only
where it changes. The renumbering also covers the account's calendars the
settings list does not show (iCal subscriptions), so they keep their
place relative to the others. Shared calendars are not offered for
reordering, their order is the owner's.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The sidebar showed the same people icon for any calendar with a share,
labelled "Share calendar", and the settings badge only gave a count, so
there was no way to tell from either who could see the calendar. A share
granted to a service account read like a share with other people.
Both now carry a hint listing the recipients ("Shared with
bot@example.com (Booking robot)"), resolved from the principals the
server lists. Until every recipient is known the hint falls back to the
count. Principals are fetched only when a calendar on screen is shared.
The per-client principal cache of the attendee availability check moves
to lib/jmap/principal-cache.ts so both use it. It no longer keeps an
empty or failed answer: the next caller asks again instead of seeing
no principals for the rest of the session.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Multi-select emails in the list, right-click, and pick "Forward as
attachments" to open one composer carrying every selected message as
its own message/rfc822 (.eml) attachment - the batch sibling of the
existing single-message action.
- lib/forward-as-attachment.ts: buildBatchForwardAsAttachmentPayload
builds one payload from many emails. The subject is count-based
(there is no single original subject), with the count localized via
an injected label so the subject never claims more attachments than
the composer carries. Emails without a blobId are skipped; null when
none has one, matching the single flow's silent no-op.
- mail-app.tsx: handleBatchForwardAsAttachment resolves the selection
through useEmailStore.getState() (fresh state, list-ordered
attachments, stale ids drop out), runs the plugin transform and veto
hooks per email - one veto cancels the whole compose - and opens the
composer with the attachments seeded via replyTo. Clears the
selection only on success.
- email-context-menu.tsx / email-list.tsx: new batch menu item shown
when the right-clicked email belongs to a multi-selection; hidden in
the scheduled view like the single item.
- locales: context_menu.forward_as_attachments and
email_composer.forward_batch_count in all 27 locales.
Co-Authored-By: Claude Code <noreply@anthropic.com>