Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
at main 3.6 kB view raw
1import { useState, useEffect, useCallback } from "react"; 2import { Folder, Plus } from "lucide-react"; 3import { getCollections } from "../api/client"; 4import { useAuth } from "../context/AuthContext"; 5import CollectionModal from "../components/CollectionModal"; 6import CollectionRow from "../components/CollectionRow"; 7 8export default function Collections() { 9 const { user } = useAuth(); 10 const [collections, setCollections] = useState([]); 11 const [loading, setLoading] = useState(true); 12 const [error, setError] = useState(null); 13 const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); 14 const [editingCollection, setEditingCollection] = useState(null); 15 16 const fetchCollections = useCallback(async () => { 17 try { 18 setLoading(true); 19 const data = await getCollections(user.did); 20 setCollections(data.items || []); 21 } catch (err) { 22 console.error(err); 23 setError("Failed to load collections"); 24 } finally { 25 setLoading(false); 26 } 27 }, [user]); 28 29 useEffect(() => { 30 if (user) { 31 fetchCollections(); 32 } 33 }, [user, fetchCollections]); 34 35 const handleCreateSuccess = () => { 36 fetchCollections(); 37 setIsCreateModalOpen(false); 38 setEditingCollection(null); 39 }; 40 41 if (loading) { 42 return ( 43 <div className="feed-page"> 44 <div 45 style={{ 46 display: "flex", 47 justifyContent: "center", 48 padding: "60px 0", 49 }} 50 > 51 <div className="spinner"></div> 52 </div> 53 </div> 54 ); 55 } 56 57 return ( 58 <div className="feed-page"> 59 <div 60 className="page-header" 61 style={{ 62 display: "flex", 63 justifyContent: "space-between", 64 alignItems: "flex-start", 65 }} 66 > 67 <div> 68 <h1 className="page-title">Collections</h1> 69 <p className="page-description"> 70 Organize your annotations, highlights, and bookmarks 71 </p> 72 </div> 73 <button 74 onClick={() => setIsCreateModalOpen(true)} 75 className="btn btn-primary" 76 > 77 <Plus size={20} /> 78 New Collection 79 </button> 80 </div> 81 82 {error ? ( 83 <div className="empty-state card"> 84 <div className="empty-state-icon"></div> 85 <h3 className="empty-state-title">Something went wrong</h3> 86 <p className="empty-state-text">{error}</p> 87 </div> 88 ) : collections.length === 0 ? ( 89 <div className="empty-state card"> 90 <div className="empty-state-icon"> 91 <Folder size={32} /> 92 </div> 93 <h3 className="empty-state-title">No collections yet</h3> 94 <p className="empty-state-text mb-6"> 95 Create your first collection to start organizing your web 96 annotations. 97 </p> 98 <button 99 onClick={() => setIsCreateModalOpen(true)} 100 className="btn btn-secondary" 101 > 102 Create Collection 103 </button> 104 </div> 105 ) : ( 106 <div className="collections-list"> 107 {collections.map((collection) => ( 108 <CollectionRow 109 key={collection.uri} 110 collection={collection} 111 onEdit={() => setEditingCollection(collection)} 112 /> 113 ))} 114 </div> 115 )} 116 117 <CollectionModal 118 isOpen={isCreateModalOpen || !!editingCollection} 119 onClose={() => { 120 setIsCreateModalOpen(false); 121 setEditingCollection(null); 122 }} 123 onSuccess={handleCreateSuccess} 124 collectionToEdit={editingCollection} 125 /> 126 </div> 127 ); 128}