import { useState, useEffect, useRef } from "react"; import { Link, useNavigate } from "react-router-dom"; import AnnotationCard, { HighlightCard } from "../components/AnnotationCard"; import { getByTarget, searchActors } from "../api/client"; import { useAuth } from "../context/AuthContext"; import { PenIcon, AlertIcon, SearchIcon } from "../components/Icons"; import { Copy, Check, ExternalLink } from "lucide-react"; export default function Url() { const { user } = useAuth(); const navigate = useNavigate(); const [url, setUrl] = useState(""); const [annotations, setAnnotations] = useState([]); const [highlights, setHighlights] = useState([]); const [loading, setLoading] = useState(false); const [searched, setSearched] = useState(false); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState("all"); const [copied, setCopied] = useState(false); const [suggestions, setSuggestions] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); const [selectedIndex, setSelectedIndex] = useState(-1); const inputRef = useRef(null); const suggestionsRef = useRef(null); useEffect(() => { const timer = setTimeout(async () => { const isUrl = url.includes("http") || url.includes("://"); if (url.length >= 2 && !isUrl) { try { const data = await searchActors(url); setSuggestions(data.actors || []); setShowSuggestions(true); } catch { // ignore } } else { setSuggestions([]); setShowSuggestions(false); } }, 300); return () => clearTimeout(timer); }, [url]); useEffect(() => { const handleClickOutside = (e) => { if ( suggestionsRef.current && !suggestionsRef.current.contains(e.target) && inputRef.current && !inputRef.current.contains(e.target) ) { setShowSuggestions(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const handleKeyDown = (e) => { if (!showSuggestions || suggestions.length === 0) return; if (e.key === "ArrowDown") { e.preventDefault(); setSelectedIndex((prev) => Math.min(prev + 1, suggestions.length - 1)); } else if (e.key === "ArrowUp") { e.preventDefault(); setSelectedIndex((prev) => Math.max(prev - 1, -1)); } else if (e.key === "Enter" && selectedIndex >= 0) { e.preventDefault(); selectSuggestion(suggestions[selectedIndex]); } else if (e.key === "Escape") { setShowSuggestions(false); } }; const selectSuggestion = (actor) => { navigate(`/profile/${encodeURIComponent(actor.handle)}`); }; const handleSearch = async (e) => { e.preventDefault(); if (!url.trim()) return; setLoading(true); setError(null); setSearched(true); const isProtocol = url.startsWith("http://") || url.startsWith("https://"); if (!isProtocol) { try { const actorRes = await searchActors(url); if (actorRes?.actors?.length > 0) { const match = actorRes.actors[0]; navigate(`/profile/${encodeURIComponent(match.handle)}`); return; } } catch { // ignore } } try { const data = await getByTarget(url); setAnnotations(data.annotations || []); setHighlights(data.highlights || []); } catch (err) { setError(err.message); } finally { setLoading(false); } }; const myAnnotations = user ? annotations.filter((a) => (a.creator?.did || a.author?.did) === user.did) : []; const myHighlights = user ? highlights.filter((h) => (h.creator?.did || h.author?.did) === user.did) : []; const myItemsCount = myAnnotations.length + myHighlights.length; const getShareUrl = () => { if (!user?.handle || !url) return null; return `${window.location.origin}/${user.handle}/url/${url}`; }; const handleCopyShareLink = async () => { const shareUrl = getShareUrl(); if (!shareUrl) return; try { await navigator.clipboard.writeText(shareUrl); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { prompt("Copy this link:", shareUrl); } }; const totalItems = annotations.length + highlights.length; const renderResults = () => { if (activeTab === "annotations" && annotations.length === 0) { return (

No annotations

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

Explore

Search for a URL to view its context layer, or find a user by their handle

setUrl(e.target.value)} onKeyDown={handleKeyDown} placeholder="https://... or handle" className="url-input" autoComplete="off" required />
{showSuggestions && suggestions.length > 0 && (
{suggestions.map((actor, index) => ( ))}
)}
{error && (

Error

{error}

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

No annotations found

Be the first to annotate this URL! Sign in to add your thoughts.

)} {searched && totalItems > 0 && ( <>

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

{user && myItemsCount > 0 && (
You have {myItemsCount} note{myItemsCount !== 1 ? "s" : ""} on this page
View
)}
{renderResults()}
)}
); }