diff --git a/src/lib/components/ShareDialog.svelte b/src/lib/components/ShareDialog.svelte index d45a0cd..2c15280 100644 --- a/src/lib/components/ShareDialog.svelte +++ b/src/lib/components/ShareDialog.svelte @@ -3,6 +3,10 @@ import X from 'lucide-svelte/icons/x'; import UserMinus from 'lucide-svelte/icons/user-minus'; import Search from 'lucide-svelte/icons/search'; + import Globe from 'lucide-svelte/icons/globe'; + import Link from 'lucide-svelte/icons/link'; + import Copy from 'lucide-svelte/icons/copy'; + import Check from 'lucide-svelte/icons/check'; import { showToast } from '$lib/stores/toast.js'; interface UserResult { @@ -15,17 +19,61 @@ noteId: string; collaborators: Collaborator[]; ownerName: string; + shareToken?: string; onClose: () => void; onUpdate: (collaborators: Collaborator[]) => void; + onShareUpdate: (token: string | null) => void; } - const { noteId, collaborators, ownerName, onClose, onUpdate }: Props = $props(); + const { noteId, collaborators, ownerName, shareToken, onClose, onUpdate, onShareUpdate }: Props = $props(); + let publicShareToken = $state(shareToken ?? null); + let copying = $state(false); + let togglingShare = $state(false); let searchQuery = $state(''); let searchResults = $state([]); let isSearching = $state(false); let searchTimeout: ReturnType | undefined; + const shareUrl = $derived(publicShareToken ? `${window.location.origin}/s/${publicShareToken}` : null); + + async function togglePublicShare() { + togglingShare = true; + try { + if (publicShareToken) { + const res = await fetch(`/api/notes/${noteId}/share`, { method: 'DELETE' }); + if (res.ok) { + publicShareToken = null; + onShareUpdate(null); + showToast('Public link removed', 'success'); + } + } else { + const res = await fetch(`/api/notes/${noteId}/share`, { method: 'POST' }); + if (res.ok) { + const data = await res.json(); + publicShareToken = data.token; + onShareUpdate(data.token); + showToast('Public link created', 'success'); + } + } + } catch { + showToast('Failed to update share link', 'error'); + } finally { + togglingShare = false; + } + } + + async function copyShareUrl() { + if (!shareUrl) return; + try { + await navigator.clipboard.writeText(shareUrl); + copying = true; + setTimeout(() => (copying = false), 2000); + } catch { + showToast('Failed to copy link', 'error'); + } + } + function handleSearch(query: string) { clearTimeout(searchTimeout); if (!query.trim()) { @@ -38,7 +86,6 @@ const res = await fetch(`/api/users/search?q=${encodeURIComponent(query)}`); if (res.ok) { const users: UserResult[] = await res.json(); - // Filter out existing collaborators const collabIds = new Set(collaborators.map((c) => c.userId)); searchResults = users.filter((u) => !collabIds.has(u.id)); } @@ -123,6 +170,60 @@ + +
+
+
+
+ + Public link +
+ +
+ + {#if publicShareToken && shareUrl} +
+
+ + {shareUrl} +
+ +
+ {/if} +
+
+ +
+