"use client"; import { useCallback, useEffect, useState } from "react"; import { toast } from "@/lib/toast"; import { AlertBanner, Button, Card, Input, Select } from "@/components/ui/Primitives"; import { createNoteShare, listNoteShares, revokeNoteShare, type PublicNoteShare } from "@/lib/notes-client"; import { exportNoteText, listCollaborators, removeCollaborator, shareNoteWithUser } from "@/lib/intake-client"; import { getWebAppOrigin } from "@/lib/product-config"; interface ShareDialogProps { noteId: string; workspaceId: string; noteTitle: string; onClose: () => void; } type CollaboratorRow = { id?: string; sharedWithUserId?: string; permission?: "view" | "comment" | "edit"; createdAt?: string; }; export function ShareDialog({ noteId, workspaceId, noteTitle, onClose }: ShareDialogProps) { const [tab, setTab] = useState<"link" | "user" | "text" | "native">("link"); const [userId, setUserId] = useState(""); const [permission, setPermission] = useState<"view" | "comment" | "edit">("view"); const [loading, setLoading] = useState(false); const [publicLinks, setPublicLinks] = useState([]); const [collaborators, setCollaborators] = useState([]); const reloadSharingState = useCallback(async () => { const [links, collabs] = await Promise.all([ listNoteShares(noteId, workspaceId).catch(() => ({ items: [] as PublicNoteShare[], total: 0 })), listCollaborators(noteId, workspaceId).catch(() => ({ items: [] as unknown[], total: 0 })), ]); setPublicLinks(links.items); setCollaborators(collabs.items as CollaboratorRow[]); }, [noteId, workspaceId]); useEffect(() => { void reloadSharingState(); }, [reloadSharingState]); async function handleCopyLink() { setLoading(true); try { const { shareToken, expiresAt } = await createNoteShare(noteId, workspaceId); const url = `${getWebAppOrigin()}/share/${shareToken}`; await navigator.clipboard.writeText(url); await reloadSharingState(); toast.success(expiresAt ? `Share link copied; expires ${new Date(expiresAt).toLocaleDateString()}` : "Share link copied"); } catch (e) { toast.error(e instanceof Error ? e.message : "Failed to create share link"); } finally { setLoading(false); } } async function handleShareWithUser() { if (!userId.trim()) { toast.error("Please enter a user ID"); return; } setLoading(true); try { await shareNoteWithUser(noteId, workspaceId, userId.trim(), permission); toast.success(`Shared with ${userId.trim()}`); setUserId(""); await reloadSharingState(); } catch (e) { toast.error(e instanceof Error ? e.message : "Failed to share"); } finally { setLoading(false); } } async function handleRevokeLink(shareToken: string) { setLoading(true); try { await revokeNoteShare(noteId, workspaceId, shareToken); await reloadSharingState(); toast.success("Public link revoked"); } catch (e) { toast.error(e instanceof Error ? e.message : "Failed to revoke link"); } finally { setLoading(false); } } async function handleRemoveCollaborator(targetUserId: string) { setLoading(true); try { await removeCollaborator(noteId, targetUserId); await reloadSharingState(); toast.success(`Access removed for ${targetUserId}`); } catch (e) { toast.error(e instanceof Error ? e.message : "Failed to remove access"); } finally { setLoading(false); } } async function handleCopyText() { setLoading(true); try { const exported = await exportNoteText(noteId, workspaceId); await navigator.clipboard.writeText(`${exported.title}\n\n${exported.plaintext}`); toast.success("Note text copied — paste in email, WhatsApp, etc."); } catch (e) { toast.error(e instanceof Error ? e.message : "Failed to export"); } finally { setLoading(false); } } async function handleNativeShare() { if (!navigator.share) { toast.error("Web Share API not supported in this browser"); return; } setLoading(true); try { const exported = await exportNoteText(noteId, workspaceId); await navigator.share({ title: noteTitle, text: exported.plaintext.slice(0, 500), url: `${getWebAppOrigin()}/notes/${noteId}`, }); } catch (e) { if ((e as Error)?.name !== "AbortError") { toast.error("Share failed"); } } finally { setLoading(false); } } const tabs = [ { key: "link" as const, label: "Copy Link" }, { key: "user" as const, label: "Share with User" }, { key: "text" as const, label: "Copy as Text" }, { key: "native" as const, label: "Share Sheet" }, ]; return (
{ if (e.target === e.currentTarget) onClose(); }} role="dialog" aria-label="Share note" >

Share Note

{tabs.map((t) => ( ))}
{tab === "link" && (

Generate a public read-only link that expires in 30 days. Revoke links you no longer want available.

{publicLinks.length === 0 ? (

No active public links.

) : (
{publicLinks.map((link) => ( Expires {link.expiresAt ? new Date(link.expiresAt).toLocaleDateString() : "when revoked"} ))}
)}
)} {tab === "user" && (

Share directly with a NoteLett user by their ID.

setUserId(e.target.value)} placeholder="User ID" aria-label="User ID to share with" />