import React, { useState, useEffect, useCallback, useRef } from "react"; import { useTranslation } from "react-i18next"; import { X, Plus, Check, Loader2, ChevronRight, FolderPlus, } from "lucide-react"; import CollectionIcon from "../common/CollectionIcon"; import { ICON_MAP } from "../common/iconMap"; import { Theme } from "emoji-picker-react"; const EmojiPicker = React.lazy(() => import("emoji-picker-react")); import { useStore } from "@nanostores/react"; import { $user } from "../../store/auth"; import { $theme } from "../../store/theme"; import { analytics } from "../../lib/analytics"; import { getCollections, addCollectionItem, createCollection, getCollectionsContaining, type Collection, } from "../../api/client"; interface AddToCollectionModalProps { isOpen: boolean; onClose: () => void; annotationUri: string; } export default function AddToCollectionModal({ isOpen, onClose, annotationUri, }: AddToCollectionModalProps) { const { t } = useTranslation(); const user = useStore($user); const theme = useStore($theme); const [collections, setCollections] = useState([]); const [loading, setLoading] = useState(true); const [addingTo, setAddingTo] = useState(null); const [addedTo, setAddedTo] = useState>(new Set()); const [error, setError] = useState(null); const sheetRef = useRef(null); const dragStartY = useRef(0); const dragCurrentY = useRef(0); const handleTouchStart = (e: React.TouchEvent) => { dragStartY.current = e.touches[0].clientY; if (sheetRef.current) sheetRef.current.style.transition = "none"; }; const handleTouchMove = (e: React.TouchEvent) => { const delta = e.touches[0].clientY - dragStartY.current; dragCurrentY.current = delta; if (delta > 0 && sheetRef.current) { sheetRef.current.style.transform = `translateY(${delta}px)`; } }; const handleTouchEnd = () => { if (sheetRef.current) { sheetRef.current.style.transition = "transform 0.3s ease"; if (dragCurrentY.current > 100) { sheetRef.current.style.transform = "translateY(100%)"; setTimeout(onClose, 300); } else { sheetRef.current.style.transform = "translateY(0)"; } } dragCurrentY.current = 0; }; const [showNewForm, setShowNewForm] = useState(false); const [newName, setNewName] = useState(""); const [newDescription, setNewDescription] = useState(""); const [newIcon, setNewIcon] = useState(""); const [activeTab, setActiveTab] = useState<"icon" | "emoji">("icon"); const [creating, setCreating] = useState(false); useEffect(() => { if (isOpen) { document.body.style.overflow = "hidden"; } return () => { document.body.style.overflow = "unset"; }; }, [isOpen]); const loadCollections = useCallback(async () => { if (!user) return; try { setLoading(true); const data = await getCollections(user.did); setCollections(data); } catch (err) { console.error(err); setError(t("addToCollection.failedLoad")); } finally { setLoading(false); } }, [user, t]); useEffect(() => { if (isOpen && user) { loadCollections(); setError(null); getCollectionsContaining(annotationUri).then((uris) => { setAddedTo(new Set(uris)); }); } }, [isOpen, user, loadCollections, annotationUri]); const handleAdd = async (collectionUri: string) => { if (addedTo.has(collectionUri)) return; try { setAddingTo(collectionUri); await addCollectionItem(collectionUri, annotationUri); setAddedTo((prev) => new Set([...prev, collectionUri])); analytics.capture("item_added_to_collection"); } catch (err) { console.error(err); setError(t("addToCollection.failedAdd")); } finally { setAddingTo(null); } }; const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); if (!newName.trim()) return; try { setCreating(true); const iconValue = newIcon ? ICON_MAP[newIcon] ? `icon:${newIcon}` : newIcon : undefined; const newCollection = await createCollection( newName.trim(), newDescription.trim() || undefined, iconValue, ); if (newCollection) { setCollections((prev) => [newCollection, ...prev]); setNewName(""); setNewDescription(""); setNewIcon(""); setShowNewForm(false); } } catch (err) { console.error(err); setError(t("addToCollection.failedCreate")); } finally { setCreating(false); } }; if (!isOpen) return null; return (
e.stopPropagation()} >

{t("addToCollection.title")}

{loading ? (

{t("addToCollection.loading")}

) : showNewForm ? (
setNewName(e.target.value)} placeholder={t("addToCollection.namePlaceholder")} autoFocus />