From f68e41d81a0f94331cf5dff1a83c8bddd404127a Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 4 May 2026 11:24:05 +0200 Subject: [PATCH] fix: enhance sharing functionality by renaming state --- .../settings/share-collection-dialog.tsx | 25 +++++++++++-------- lib/jmap/client.ts | 6 +++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/components/settings/share-collection-dialog.tsx b/components/settings/share-collection-dialog.tsx index 4ef3022a..e05ea947 100644 --- a/components/settings/share-collection-dialog.tsx +++ b/components/settings/share-collection-dialog.tsx @@ -79,7 +79,7 @@ export function ShareCollectionDialog({ const t = useTranslations("sharing"); const tCommon = useTranslations("common"); const modalRef = useRef(null); - const [principals, setPrincipals] = useState([]); + const [allPrincipals, setAllPrincipals] = useState([]); const [loadingPrincipals, setLoadingPrincipals] = useState(true); const [search, setSearch] = useState(""); const [savingId, setSavingId] = useState(null); @@ -91,23 +91,28 @@ export function ShareCollectionDialog({ setLoadingPrincipals(true); client.getPrincipals().then((list) => { if (cancelled) return; - // Exclude the user themselves and any principal that already has a share - const existing = new Set(Object.keys(shareWith || {})); - const filtered = list.filter((p) => p.id !== ownAccountId && !existing.has(p.id)); - setPrincipals(filtered); + setAllPrincipals(list); setLoadingPrincipals(false); }).catch(() => { if (!cancelled) setLoadingPrincipals(false); }); return () => { cancelled = true; }; - }, [client, ownAccountId, shareWith]); + }, [client]); - // Map principal id -> Principal for displayed shares + // Map of every fetched principal by id, used for name/description lookups in + // the shared list. Must include principals that already have a share so the + // list shows their name rather than the raw id. const allPrincipalsById = useMemo(() => { const map = new Map(); - for (const p of principals) map.set(p.id, p); + for (const p of allPrincipals) map.set(p.id, p); return map; - }, [principals]); + }, [allPrincipals]); + + // Principals available to add: exclude self and anyone already shared with. + const principals = useMemo(() => { + const existing = new Set(Object.keys(shareWith || {})); + return allPrincipals.filter((p) => p.id !== ownAccountId && !existing.has(p.id)); + }, [allPrincipals, ownAccountId, shareWith]); // Close on Escape, focus trap, click outside useEffect(() => { @@ -155,8 +160,6 @@ export function ShareCollectionDialog({ setSavingId(principal.id); try { await onShare(principal.id, rights); - // Move principal out of the "to add" list - setPrincipals((prev) => prev.filter((p) => p.id !== principal.id)); setShowAdd(false); setSearch(""); toast.success(t("share_added")); diff --git a/lib/jmap/client.ts b/lib/jmap/client.ts index 3faf65dc..45d83f2a 100644 --- a/lib/jmap/client.ts +++ b/lib/jmap/client.ts @@ -3314,6 +3314,9 @@ export class JMAPClient implements IJMAPClient { const err = result.notUpdated[calendarId]; throw new Error(err.description || "Failed to update calendar share"); } + if (!result?.updated || !(calendarId in result.updated)) { + throw new Error("Server did not confirm the share update"); + } } /** @@ -3339,6 +3342,9 @@ export class JMAPClient implements IJMAPClient { const err = result.notUpdated[addressBookId]; throw new Error(err.description || "Failed to update address book share"); } + if (!result?.updated || !(addressBookId in result.updated)) { + throw new Error("Server did not confirm the share update"); + } } private async fetchPaginatedContacts(