"use client" import { useState, useEffect, Suspense } from "react" import { useSearchParams, useRouter } from "next/navigation" import Link from "next/link" import { useSession } from "@/lib/auth-client" import { QrCodeIcon, CheckCircle2, Loader2, AlertCircle, Gamepad2, ShieldCheck, ArrowRight, } from "lucide-react" import { motion } from "motion/react" function PairContent() { const searchParams = useSearchParams() const router = useRouter() const token = searchParams.get("token") const { data: session, isPending } = useSession() const [status, setStatus] = useState<"idle" | "confirming" | "success" | "error">("idle") const [error, setError] = useState(null) const [checkedToken, setCheckedToken] = useState(false) const [tokenValid, setTokenValid] = useState(null) // Validate the token exists on the server before showing the confirm UI useEffect(() => { if (!token) { setCheckedToken(true) setTokenValid(false) return } let cancelled = false fetch(`/api/plugin/pair/status/${encodeURIComponent(token)}`) .then(async (res) => { if (cancelled) return const data = await res.json() if (data.status === "pending") { setTokenValid(true) } else if (data.status === "confirmed") { setTokenValid(false) setError("This pairing link has already been used.") } else { setTokenValid(false) setError(data.error || "This pairing link is invalid or expired.") } }) .catch(() => { if (!cancelled) { setTokenValid(false) setError("Could not verify pairing link.") } }) .finally(() => { if (!cancelled) setCheckedToken(true) }) return () => { cancelled = true } }, [token]) async function handleConfirm() { if (!token) return setStatus("confirming") setError(null) try { const res = await fetch("/api/plugin/pair/confirm", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token }), }) const data = await res.json() if (!res.ok) { setStatus("error") setError(data.error || "Failed to link plugin.") return } setStatus("success") } catch { setStatus("error") setError("Network error. Please try again.") } } // ── Loading ─────────────────────────────────────────────── if (isPending || !checkedToken) { return (
) } // ── No token ────────────────────────────────────────────── if (!token || tokenValid === false) { return (

Invalid Pairing Link

{error || "This link is missing a token or has expired. Start a new pairing session from the DeckyVault plugin on your Steam Deck."}

Go Home
) } // ── Not logged in ───────────────────────────────────────── if (!session) { const callback = encodeURIComponent(`/pair?token=${token}`) return (

Log in to link your plugin

You need to be logged in to DeckyVault so we can create an API key for your account.

Log In Create Account
) } // ── Success ─────────────────────────────────────────────── if (status === "success") { return (

Plugin Linked!

Your DeckyVault plugin is now connected to{" "} {session.user.name} 's account. An API key named{" "} Decky Loader Plugin{" "} was created. You can close this page and return to your Steam Deck.

Manage API Keys
) } // ── Confirm ─────────────────────────────────────────────── return (

Link DeckyVault Plugin

Confirm to connect the DeckyVault plugin on your Steam Deck to your account. We'll create an API key so the plugin can upload performance entries on your behalf.

{session.user.image ? ( // eslint-disable-next-line @next/next/no-img-element ) : (
{session.user.name?.[0]?.toUpperCase()}
)}

{session.user.name}

{session.user.email}

An API key (Decky Loader Plugin) will be created for your account. You can revoke it anytime from Settings → API Keys.
{error && (
{error}
)}
) } function CenterCard({ children }: { children: React.ReactNode }) { return (
{children}
) } export default function PairPage() { return ( } > ) }