1import { useState, useEffect } from "react";
2import { Link } from "react-router-dom";
3import { useAuth } from "../context/AuthContext";
4import { getUserHighlights, deleteHighlight } from "../api/client";
5import { HighlightIcon } from "../components/Icons";
6import { HighlightCard } from "../components/AnnotationCard";
7
8export default function Highlights() {
9 const { user, isAuthenticated, loading } = useAuth();
10 const [highlights, setHighlights] = useState([]);
11 const [loadingHighlights, setLoadingHighlights] = useState(true);
12 const [error, setError] = useState(null);
13
14 useEffect(() => {
15 async function loadHighlights() {
16 if (!user?.did) return;
17
18 try {
19 setLoadingHighlights(true);
20 const data = await getUserHighlights(user.did);
21 setHighlights(data.items || []);
22 } catch (err) {
23 console.error("Failed to load highlights:", err);
24 setError(err.message);
25 } finally {
26 setLoadingHighlights(false);
27 }
28 }
29
30 if (isAuthenticated && user) {
31 loadHighlights();
32 }
33 }, [isAuthenticated, user]);
34
35 const handleDelete = async (uri) => {
36 if (!confirm("Delete this highlight?")) return;
37
38 try {
39 const parts = uri.split("/");
40 const rkey = parts[parts.length - 1];
41 await deleteHighlight(rkey);
42 setHighlights((prev) => prev.filter((h) => (h.id || h.uri) !== uri));
43 } catch (err) {
44 alert("Failed to delete: " + err.message);
45 }
46 };
47
48 if (loading)
49 return (
50 <div className="page-loading">
51 <div className="spinner"></div>
52 </div>
53 );
54
55 if (!isAuthenticated) {
56 return (
57 <div className="new-page">
58 <div className="card" style={{ textAlign: "center", padding: "48px" }}>
59 <h2>Sign in to view your highlights</h2>
60 <p style={{ color: "var(--text-secondary)", marginTop: "8px" }}>
61 You need to be logged in with your Bluesky account
62 </p>
63 <Link
64 to="/login"
65 className="btn btn-primary"
66 style={{ marginTop: "24px" }}
67 >
68 Sign in with Bluesky
69 </Link>
70 </div>
71 </div>
72 );
73 }
74
75 return (
76 <div className="feed-page">
77 <div className="page-header">
78 <h1 className="page-title">My Highlights</h1>
79 <p className="page-description">
80 Text you've highlighted across the web
81 </p>
82 </div>
83
84 {loadingHighlights ? (
85 <div className="feed">
86 {[1, 2, 3].map((i) => (
87 <div key={i} className="card">
88 <div
89 className="skeleton skeleton-text"
90 style={{ width: "40%" }}
91 ></div>
92 <div className="skeleton skeleton-text"></div>
93 <div
94 className="skeleton skeleton-text"
95 style={{ width: "60%" }}
96 ></div>
97 </div>
98 ))}
99 </div>
100 ) : error ? (
101 <div className="empty-state">
102 <div className="empty-state-icon">⚠️</div>
103 <h3 className="empty-state-title">Error loading highlights</h3>
104 <p className="empty-state-text">{error}</p>
105 </div>
106 ) : highlights.length === 0 ? (
107 <div className="empty-state">
108 <div className="empty-state-icon">
109 <HighlightIcon size={32} />
110 </div>
111 <h3 className="empty-state-title">No highlights yet</h3>
112 <p className="empty-state-text">
113 Highlight text on any page using the browser extension.
114 </p>
115 </div>
116 ) : (
117 <div className="feed">
118 {highlights.map((highlight) => (
119 <HighlightCard
120 key={highlight.id}
121 highlight={highlight}
122 onDelete={handleDelete}
123 />
124 ))}
125 </div>
126 )}
127 </div>
128 );
129}