feat: add JMAP sharing for calendars and address books
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import type { Calendar, CalendarEvent, CalendarParticipant } from '@/lib/jmap/types';
|
||||
import type { Calendar, CalendarEvent, CalendarParticipant, CalendarRights } from '@/lib/jmap/types';
|
||||
import { debug } from '@/lib/debug';
|
||||
import { normalizeAllDayDuration } from '@/lib/calendar-utils';
|
||||
import { parseDuration } from '@/components/calendar/event-card';
|
||||
@@ -125,6 +125,7 @@ interface CalendarStore {
|
||||
rsvpEvent: (client: IJMAPClient, eventId: string, participantId: string, status: string, replyTo?: Record<string, string> | null) => Promise<void>;
|
||||
importEvents: (client: IJMAPClient, events: Partial<CalendarEvent>[], calendarId: string) => Promise<number>;
|
||||
updateCalendar: (client: IJMAPClient, calendarId: string, updates: Partial<Calendar>) => Promise<void>;
|
||||
shareCalendar: (client: IJMAPClient, calendarId: string, principalId: string, rights: CalendarRights | null) => Promise<void>;
|
||||
createCalendar: (client: IJMAPClient, calendar: Partial<Calendar>) => Promise<Calendar | null>;
|
||||
removeCalendar: (client: IJMAPClient, calendarId: string) => Promise<void>;
|
||||
clearCalendarEvents: (client: IJMAPClient, calendarId: string) => Promise<number>;
|
||||
@@ -653,6 +654,29 @@ export const useCalendarStore = create<CalendarStore>()(
|
||||
}
|
||||
},
|
||||
|
||||
shareCalendar: async (client, calendarId, principalId, rights) => {
|
||||
set({ error: null });
|
||||
try {
|
||||
const cal = get().calendars.find(c => c.id === calendarId);
|
||||
const realId = cal?.originalId || calendarId;
|
||||
const targetAccountId = cal?.accountId;
|
||||
await client.setCalendarShare(realId, principalId, rights, targetAccountId);
|
||||
set((state) => ({
|
||||
calendars: state.calendars.map(c => {
|
||||
if (c.id !== calendarId) return c;
|
||||
const next = { ...(c.shareWith ?? {}) };
|
||||
if (rights === null) delete next[principalId];
|
||||
else next[principalId] = rights;
|
||||
return { ...c, shareWith: next };
|
||||
}),
|
||||
}));
|
||||
} catch (error) {
|
||||
debug.error('Failed to share calendar:', error);
|
||||
set({ error: 'Failed to share calendar' });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
createCalendar: async (client, calendar) => {
|
||||
set({ error: null });
|
||||
try {
|
||||
|
||||
+42
-1
@@ -1,6 +1,6 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { ContactCard, AddressBook, ContactName } from '@/lib/jmap/types';
|
||||
import type { ContactCard, AddressBook, AddressBookRights, ContactName } from '@/lib/jmap/types';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import { generateUUID } from '@/lib/utils';
|
||||
import { debug } from '@/lib/debug';
|
||||
@@ -101,6 +101,8 @@ interface ContactStore {
|
||||
bulkAddToGroup: (client: IJMAPClient | null, groupId: string, contactIds: string[]) => Promise<void>;
|
||||
moveContactToAddressBook: (client: IJMAPClient, contactIds: string[], addressBook: AddressBook) => Promise<void>;
|
||||
renameAddressBook: (client: IJMAPClient, addressBook: AddressBook, newName: string) => Promise<void>;
|
||||
removeAddressBook: (client: IJMAPClient, addressBook: AddressBook) => Promise<void>;
|
||||
shareAddressBook: (client: IJMAPClient, addressBook: AddressBook, principalId: string, rights: AddressBookRights | null) => Promise<void>;
|
||||
renameKeyword: (client: IJMAPClient | null, oldKeyword: string, newKeyword: string) => Promise<void>;
|
||||
|
||||
importContacts: (client: IJMAPClient | null, contacts: ContactCard[]) => Promise<number>;
|
||||
@@ -655,6 +657,45 @@ export const useContactStore = create<ContactStore>()(
|
||||
}
|
||||
},
|
||||
|
||||
removeAddressBook: async (client, addressBook) => {
|
||||
set({ error: null });
|
||||
try {
|
||||
const originalId = addressBook.originalId || addressBook.id;
|
||||
const accountId = addressBook.isShared ? addressBook.accountId : undefined;
|
||||
await client.deleteAddressBook(originalId, accountId);
|
||||
set((state) => ({
|
||||
addressBooks: state.addressBooks.filter(b => b.id !== addressBook.id),
|
||||
contacts: state.contacts.filter(c => !c.addressBookIds?.[addressBook.id]),
|
||||
}));
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : 'Failed to delete address book';
|
||||
set({ error: msg });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
shareAddressBook: async (client, addressBook, principalId, rights) => {
|
||||
set({ error: null });
|
||||
try {
|
||||
const originalId = addressBook.originalId || addressBook.id;
|
||||
const accountId = addressBook.isShared ? addressBook.accountId : undefined;
|
||||
await client.setAddressBookShare(originalId, principalId, rights, accountId);
|
||||
set((state) => ({
|
||||
addressBooks: state.addressBooks.map(b => {
|
||||
if (b.id !== addressBook.id) return b;
|
||||
const next = { ...(b.shareWith ?? {}) };
|
||||
if (rights === null) delete next[principalId];
|
||||
else next[principalId] = rights;
|
||||
return { ...b, shareWith: next };
|
||||
}),
|
||||
}));
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : 'Failed to share address book';
|
||||
set({ error: msg });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
renameKeyword: async (client, oldKeyword, newKeyword) => {
|
||||
set({ error: null });
|
||||
const oldKw = oldKeyword.trim();
|
||||
|
||||
Reference in New Issue
Block a user