import { useState, useEffect, useRef } from "react"; import { Link } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; import { searchActors, startLogin } from "../api/client"; import { AtSign } from "lucide-react"; import logo from "../assets/logo.svg"; import SignUpModal from "../components/SignUpModal"; export default function Login() { const { isAuthenticated, user, logout } = useAuth(); const [showSignUp, setShowSignUp] = useState(false); const [handle, setHandle] = useState(""); const [inviteCode, setInviteCode] = useState(""); const [showInviteInput, setShowInviteInput] = useState(false); const [suggestions, setSuggestions] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [selectedIndex, setSelectedIndex] = useState(-1); const inputRef = useRef(null); const inviteRef = useRef(null); const suggestionsRef = useRef(null); const [providerIndex, setProviderIndex] = useState(0); const [morphClass, setMorphClass] = useState("morph-in"); const providers = [ "AT Protocol", "Bluesky", "Blacksky", "Tangled", "Northsky", "witchcraft.systems", "topphie.social", "altq.net", ]; useEffect(() => { const cycleText = () => { setMorphClass("morph-out"); setTimeout(() => { setProviderIndex((prev) => (prev + 1) % providers.length); setMorphClass("morph-in"); }, 400); }; const interval = setInterval(cycleText, 3000); return () => clearInterval(interval); }, [providers.length]); const isSelectionRef = useRef(false); useEffect(() => { if (handle.length >= 3) { if (isSelectionRef.current) { isSelectionRef.current = false; return; } const timer = setTimeout(async () => { try { const data = await searchActors(handle); setSuggestions(data.actors || []); setShowSuggestions(true); setSelectedIndex(-1); } catch (e) { console.error("Search failed:", e); } }, 300); return () => clearTimeout(timer); } }, [handle]); 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); }, []); if (isAuthenticated) { return (
{user?.avatar ? ( {user.displayName ) : ( {(user?.displayName || user?.handle || "??") .substring(0, 2) .toUpperCase()} )}

Welcome back, {user?.displayName || user?.handle}

View Profile
); } 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) => { isSelectionRef.current = true; setHandle(actor.handle); setSuggestions([]); setShowSuggestions(false); inputRef.current?.blur(); }; const handleSubmit = async (e) => { e.preventDefault(); if (!handle.trim()) return; if (showInviteInput && !inviteCode.trim()) return; setLoading(true); setError(null); try { const result = await startLogin(handle.trim(), inviteCode.trim()); if (result.authorizationUrl) { window.location.href = result.authorizationUrl; } } catch (err) { console.error("Login error:", err); if ( err.message && (err.message.includes("invite_required") || err.message.includes("Invite code required")) ) { setShowInviteInput(true); setError("Please enter an invite code to continue."); setTimeout(() => inviteRef.current?.focus(), 100); } else { setError(err.message || "Failed to start login"); } setLoading(false); } }; return (
Margin Logo X

Sign in with your{" "} {providers[providerIndex]} {" "} handle

{ const val = e.target.value; setHandle(val); if (val.length < 3) { setSuggestions([]); setShowSuggestions(false); } }} onKeyDown={handleKeyDown} onFocus={() => handle.length >= 3 && suggestions.length > 0 && !handle.includes(".") && setShowSuggestions(true) } autoComplete="off" autoCapitalize="off" autoCorrect="off" spellCheck="false" disabled={loading} /> {showSuggestions && suggestions.length > 0 && (
{suggestions.map((actor, index) => ( ))}
)}
{showInviteInput && (
setInviteCode(e.target.value)} autoComplete="off" disabled={loading} style={{ borderColor: "var(--accent)" }} />
)} {error &&

{error}

}

By signing in, you agree to our{" "} Terms of Service and{" "} Privacy Policy.

or
{showSignUp && setShowSignUp(false)} />}
); }