"use client"; import { useEffect, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { LogIn, Puzzle, Smartphone, RefreshCw, Link2 } from "lucide-react"; import { QRCodeSVG } from "qrcode.react"; import { useAuth } from "@/hooks/useAuth"; import { Navbar } from "@/components/public/Navbar"; import { Footer } from "@/components/public/Footer"; import { generateNostrConnectSetup, waitForNostrConnectSigner, } from "@/lib/nostr"; type Tab = "extension" | "external"; export default function LoginPage() { const { user, loading, login, loginWithBunker, loginWithConnectedSigner } = useAuth(); const router = useRouter(); const [activeTab, setActiveTab] = useState("extension"); const [error, setError] = useState(""); const [loggingIn, setLoggingIn] = useState(false); // Extension tab // (no extra state needed) // External signer tab — QR section const [qrUri, setQrUri] = useState(null); const [qrStatus, setQrStatus] = useState< "generating" | "waiting" | "connecting" >("generating"); const qrAbortRef = useRef(null); const qrSecretRef = useRef(null); // External signer tab — bunker URI section const [bunkerInput, setBunkerInput] = useState(""); useEffect(() => { if (!loading && user) redirectByRole(user.role); }, [user, loading]); function redirectByRole(role: string) { if (role === "ADMIN" || role === "MODERATOR") { router.push("/admin/overview"); } else { router.push("/dashboard"); } } // Start (or restart) the nostrconnect QR flow async function startQrFlow() { qrAbortRef.current?.abort(); const controller = new AbortController(); qrAbortRef.current = controller; setQrUri(null); setQrStatus("generating"); setError(""); try { const { uri, clientSecretKey } = await generateNostrConnectSetup(); if (controller.signal.aborted) return; qrSecretRef.current = clientSecretKey; setQrUri(uri); setQrStatus("waiting"); const { signer } = await waitForNostrConnectSigner( clientSecretKey, uri, controller.signal ); if (controller.signal.aborted) return; setQrStatus("connecting"); setLoggingIn(true); const loggedInUser = await loginWithConnectedSigner(signer); await signer.close().catch(() => {}); redirectByRole(loggedInUser.role); } catch (err: any) { if (controller.signal.aborted) return; setError(err.message || "Connection failed"); setQrStatus("waiting"); setLoggingIn(false); } } // Launch / restart QR when switching to external tab useEffect(() => { if (activeTab !== "external") { qrAbortRef.current?.abort(); return; } startQrFlow(); return () => { qrAbortRef.current?.abort(); }; }, [activeTab]); const handleExtensionLogin = async () => { setError(""); setLoggingIn(true); try { const loggedInUser = await login(); redirectByRole(loggedInUser.role); } catch (err: any) { setError(err.message || "Login failed"); } finally { setLoggingIn(false); } }; const handleBunkerLogin = async () => { if (!bunkerInput.trim()) return; setError(""); setLoggingIn(true); try { const loggedInUser = await loginWithBunker(bunkerInput.trim()); redirectByRole(loggedInUser.role); } catch (err: any) { setError(err.message || "Connection failed"); } finally { setLoggingIn(false); } }; if (loading) { return ( <>
Loading...