Margin is an open annotation layer for the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
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 const handleDelete = () => {
42 fetchCollections();
43 setEditingCollection(null);
44 };
45
46 if (loading) {
47 return (
48 <div className="feed-page">
49 <div
50 style={{
51 display: "flex",
52 justifyContent: "center",
53 padding: "60px 0",
54 }}
55 >
56 <div className="spinner"></div>
57 </div>
58 </div>
59 );
60 }
61
62 return (
63 <div className="feed-page">
64 <div
65 className="page-header"
66 style={{
67 display: "flex",
68 justifyContent: "space-between",
69 alignItems: "flex-start",
70 }}
71 >
72 <div>
73 <h1 className="page-title">Collections</h1>
74 <p className="page-description">
75 Organize your annotations, highlights, and bookmarks
76 </p>
77 </div>
78 <button
79 onClick={() => setIsCreateModalOpen(true)}
80 className="btn btn-primary"
81 >
82 <Plus size={20} />
83 New Collection
84 </button>
85 </div>
86
87 {error ? (
88 <div className="empty-state card">
89 <div className="empty-state-icon">⚠️</div>
90 <h3 className="empty-state-title">Something went wrong</h3>
91 <p className="empty-state-text">{error}</p>
92 </div>
93 ) : collections.length === 0 ? (
94 <div className="empty-state card">
95 <div className="empty-state-icon">
96 <Folder size={32} />
97 </div>
98 <h3 className="empty-state-title">No collections yet</h3>
99 <p className="empty-state-text mb-6">
100 Create your first collection to start organizing your web
101 annotations.
102 </p>
103 <button
104 onClick={() => setIsCreateModalOpen(true)}
105 className="btn btn-secondary"
106 >
107 Create Collection
108 </button>
109 </div>
110 ) : (
111 <div className="collections-list">
112 {collections.map((collection) => (
113 <CollectionRow
114 key={collection.uri}
115 collection={collection}
116 onEdit={() => setEditingCollection(collection)}
117 />
118 ))}
119 </div>
120 )}
121
122 <CollectionModal
123 isOpen={isCreateModalOpen || !!editingCollection}
124 onClose={() => {
125 setIsCreateModalOpen(false);
126 setEditingCollection(null);
127 }}
128 onSuccess={handleCreateSuccess}
129 onDelete={handleDelete}
130 collectionToEdit={editingCollection}
131 />
132 </div>
133 );
134}