From 14cdb2f6ffc3a82ff3b2f44bc7c1f39b4377ba64 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:07:47 +0200 Subject: [PATCH] feat: organize invitations as the default ParticipantIdentity --- components/calendar/calendar-app.tsx | 13 +++++-- components/settings/calendar-settings.tsx | 36 ++++++++++++++++++- lib/__tests__/jmap-client-extensions.test.ts | 26 ++++++++++++++ lib/jmap/client-interface.ts | 6 +++- lib/jmap/client.ts | 38 +++++++++++++++++++- lib/jmap/types.ts | 10 ++++-- locales/ar/common.json | 3 ++ locales/ca/common.json | 3 ++ locales/cs/common.json | 3 ++ locales/da/common.json | 3 ++ locales/de/common.json | 3 ++ locales/en/common.json | 3 ++ locales/es/common.json | 3 ++ locales/fa/common.json | 3 ++ locales/fr/common.json | 3 ++ locales/he/common.json | 3 ++ locales/hu/common.json | 3 ++ locales/it/common.json | 3 ++ locales/ja/common.json | 3 ++ locales/ko/common.json | 3 ++ locales/lv/common.json | 3 ++ locales/mn/common.json | 3 ++ locales/nb/common.json | 3 ++ locales/nl/common.json | 3 ++ locales/pl/common.json | 3 ++ locales/pt/common.json | 3 ++ locales/ro/common.json | 3 ++ locales/ru/common.json | 3 ++ locales/sk/common.json | 3 ++ locales/tr/common.json | 3 ++ locales/uk/common.json | 3 ++ locales/zh-TW/common.json | 3 ++ locales/zh/common.json | 3 ++ stores/calendar-store.ts | 30 +++++++++++++++- 34 files changed, 232 insertions(+), 8 deletions(-) diff --git a/components/calendar/calendar-app.tsx b/components/calendar/calendar-app.tsx index 11f2436db..ecd136b64 100644 --- a/components/calendar/calendar-app.tsx +++ b/components/calendar/calendar-app.tsx @@ -137,9 +137,18 @@ export function CalendarApp({ linkSegments }: CalendarAppProps = {}) { void fetchPrincipal(); }, [accountEmails, fetchPrincipal]); + // The default ParticipantIdentity (draft-ietf-jmap-calendars §6) is the + // address new invitations are organised from, so it goes first: the event + // modal takes currentUserEmails[0] as organizer. + const participantIdentities = useCalendarStore((s) => s.participantIdentities); const currentUserEmails = useMemo( - () => collectUserCalendarAddresses(identities.map(id => id.email), accountEmails), - [identities, accountEmails] + () => collectUserCalendarAddresses( + participantIdentities.filter(i => i.isDefault).map(i => i.calendarAddress.replace(/^mailto:/i, '')), + participantIdentities.map(i => i.calendarAddress.replace(/^mailto:/i, '')), + identities.map(id => id.email), + accountEmails, + ), + [participantIdentities, identities, accountEmails] ); const [showEventModal, setShowEventModal] = useState(false); diff --git a/components/settings/calendar-settings.tsx b/components/settings/calendar-settings.tsx index 4154f17b5..c841d26fb 100644 --- a/components/settings/calendar-settings.tsx +++ b/components/settings/calendar-settings.tsx @@ -1,16 +1,30 @@ "use client"; +import { useEffect } from 'react'; import { useTranslations } from 'next-intl'; import { useCalendarStore, CalendarViewMode } from '@/stores/calendar-store'; import { useSettingsStore } from '@/stores/settings-store'; import { usePolicyStore } from '@/stores/policy-store'; +import { useAuthStore } from '@/stores/auth-store'; +import { toast } from '@/stores/toast-store'; import { SettingsSection, SettingItem, Select, ToggleSwitch } from './settings-section'; export function CalendarSettings() { const t = useTranslations('calendar.settings'); const tViews = useTranslations('calendar.views'); - const { viewMode, setViewMode } = useCalendarStore(); + const { viewMode, setViewMode, participantIdentities, fetchParticipantIdentities, setDefaultParticipantIdentity } = useCalendarStore(); + const client = useAuthStore((s) => s.client); + + // ParticipantIdentity list (draft-ietf-jmap-calendars §6): which of the + // user's addresses organises new invitations. Loaded here because the + // settings page can open before the calendar app ever did. + useEffect(() => { + if (client && participantIdentities.length === 0) { + void fetchParticipantIdentities(client); + } + }, [client, participantIdentities.length, fetchParticipantIdentities]); + const defaultIdentityId = participantIdentities.find((i) => i.isDefault)?.id ?? participantIdentities[0]?.id ?? ''; const { showTimeInMonthView, showWeekNumbers, @@ -37,6 +51,26 @@ export function CalendarSettings() { /> + {client && participantIdentities.length > 1 && ( + +