"use client"; import { useEffect, useMemo, useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { Avatar } from "@/components/ui/avatar"; import { X, Loader2, UserPlus, Trash2, Users, ChevronDown, } from "lucide-react"; import type { IJMAPClient } from "@/lib/jmap/client-interface"; import type { Principal } from "@/lib/jmap/types"; import { useSharingStore, type SharedResourceKind } from "@/stores/sharing-store"; export interface ShareFolderDialogProps { client: IJMAPClient; resourceId: string; resourceName: string; resourceKind: SharedResourceKind; onClose: () => void; } const PRESET_OPTIONS: Record = { mailbox: ["read", "readWrite", "manager"], calendar: ["read", "readWrite", "manager"], addressBook: ["read", "readWrite", "manager"], file: ["read", "readWrite", "manager"], }; export function ShareFolderDialog({ client, resourceId, resourceName, resourceKind, onClose, }: ShareFolderDialogProps) { const t = useTranslations("sharing"); const tCommon = useTranslations("common"); const modalRef = useRef(null); const sharedByMe = useSharingStore((s) => s.sharedByMe); const loadPrincipals = useSharingStore((s) => s.loadPrincipals); const shareFolder = useSharingStore((s) => s.shareFolder); const revokeShare = useSharingStore((s) => s.revokeShare); const changeRole = useSharingStore((s) => s.changeRole); const [allPrincipals, setAllPrincipals] = useState([]); const [loadingPrincipals, setLoadingPrincipals] = useState(true); const [search, setSearch] = useState(""); const [savingId, setSavingId] = useState(null); const [showAdd, setShowAdd] = useState(false); const [message, setMessage] = useState(""); useEffect(() => { let cancelled = false; setLoadingPrincipals(true); loadPrincipals(client) .then((list) => { if (cancelled) return; setAllPrincipals(list); setLoadingPrincipals(false); }) .catch(() => { if (!cancelled) setLoadingPrincipals(false); }); return () => { cancelled = true; }; }, [client, loadPrincipals]); const ownAccountId = client.getAccountId(); const allPrincipalsById = useMemo(() => { const map = new Map(); for (const p of allPrincipals) map.set(p.id, p); return map; }, [allPrincipals]); const currentShares = sharedByMe.filter( (f) => f.resourceId === resourceId && f.resourceKind === resourceKind, ); const principals = useMemo(() => { const existing = new Set(currentShares.map((s) => s.principalId)); return allPrincipals.filter( (p) => p.id !== ownAccountId && !existing.has(p.id), ); }, [allPrincipals, ownAccountId, currentShares]); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey); }, [onClose]); const handleRemove = async (principalId: string) => { setSavingId(principalId); try { await revokeShare(client, resourceId, resourceKind, principalId); } catch { /* error toast comes from store */ } finally { setSavingId(null); } }; const handleChangeRole = async (principalId: string, role: string) => { setSavingId(principalId); try { await changeRole(client, resourceId, resourceKind, principalId, role); } catch { /* error toast comes from store */ } finally { setSavingId(null); } }; const handleAdd = async (principal: Principal) => { setSavingId(principal.id); try { await shareFolder( client, resourceId, resourceName, resourceKind, principal.id, "read", message || undefined, ); setShowAdd(false); setSearch(""); setMessage(""); } catch { /* error toast comes from store */ } finally { setSavingId(null); } }; const filteredPrincipals = useMemo(() => { const q = search.trim().toLowerCase(); if (!q) return principals; return principals.filter( (p) => p.name.toLowerCase().includes(q) || p.email?.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q), ); }, [principals, search]); const presetOptions = PRESET_OPTIONS[resourceKind]; const kindLabels: Record = { mailbox: "Mail folder", calendar: "Calendar", addressBook: "Address book", file: "File folder", }; return (