import { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; import AnnotationCard, { HighlightCard } from "../components/AnnotationCard"; import { getUserTargetItems } from "../api/client"; import { PenIcon, HighlightIcon, SearchIcon, BlueskyIcon, } from "../components/Icons"; export default function UserUrl() { const { handle, "*": urlPath } = useParams(); const targetUrl = urlPath || ""; const [profile, setProfile] = useState(null); const [annotations, setAnnotations] = useState([]); const [highlights, setHighlights] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState("all"); useEffect(() => { async function fetchData() { if (!targetUrl) { setLoading(false); return; } try { setLoading(true); setError(null); const profileRes = await fetch( `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(handle)}`, ); let did = handle; if (profileRes.ok) { const profileData = await profileRes.json(); setProfile(profileData); did = profileData.did; } const data = await getUserTargetItems(did, targetUrl); setAnnotations(data.annotations || []); setHighlights(data.highlights || []); } catch (err) { setError(err.message); } finally { setLoading(false); } } fetchData(); }, [handle, targetUrl]); const displayName = profile?.displayName || profile?.handle || handle; const displayHandle = profile?.handle || (handle?.startsWith("did:") ? null : handle); const avatarUrl = profile?.avatar; const getInitial = () => { return (displayName || displayHandle || "??") ?.substring(0, 2) .toUpperCase(); }; const totalItems = annotations.length + highlights.length; const bskyProfileUrl = displayHandle ? `https://bsky.app/profile/${displayHandle}` : `https://bsky.app/profile/${handle}`; const renderResults = () => { if (activeTab === "annotations" && annotations.length === 0) { return (

No annotations

); } if (activeTab === "highlights" && highlights.length === 0) { return (

No highlights

); } return ( <> {(activeTab === "all" || activeTab === "annotations") && annotations.map((a) => )} {(activeTab === "all" || activeTab === "highlights") && highlights.map((h) => )} ); }; if (!targetUrl) { return (

No URL specified

Please provide a URL to view annotations.

); } return (
{avatarUrl ? ( {displayName} ) : ( {getInitial()} )}

{displayName}

{displayHandle && ( @{displayHandle} )}
Annotations on: {targetUrl}
{loading && (
{[1, 2, 3].map((i) => (
))}
)} {error && (
⚠️

Error

{error}

)} {!loading && !error && totalItems === 0 && (

No items found

{displayName} hasn't annotated this page yet.

)} {!loading && !error && totalItems > 0 && ( <>

{totalItems} item{totalItems !== 1 ? "s" : ""}

{renderResults()}
)}
); }