"use client"; /** * AuthenticationSection — the connect-accounts / email / password controls * for the /settings/account page. * * Split out of the old monolithic /settings page so account & security lives * on its own dedicated screen. OAuth link flows return to /settings/account * (via `next`), and the post-link banner (LinkResult) reads/clears the * `?link=` param there. */ import { Suspense, useEffect, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { AlertCircle, Check } from "lucide-react"; import { auth, AuthError } from "@/lib/auth"; import { useAuth } from "@/components/auth/auth-provider"; import { AddEmailModal } from "@/components/auth/add-email-modal"; import { ChangePasswordModal } from "@/components/auth/change-password-modal"; import { Row } from "@/components/settings/settings-ui"; const BFF_BASE = process.env.NEXT_PUBLIC_BFF_URL || "http://localhost:8080"; /** Where OAuth link flows and the result banner return to. */ const RETURN_TO = "/settings/account"; export function AuthenticationSection() { const { user } = useAuth(); const [pwModalOpen, setPwModalOpen] = useState(false); const [emailModalOpen, setEmailModalOpen] = useState(false); const [linkedProviders, setLinkedProviders] = useState | null>( null, ); // Fetch linked providers on mount. A null set means "not yet loaded" — we // render the rows as if nothing is connected until the answer comes back, // which is fine because the only thing that changes after load is the // "Connected" pill swapping in for "Connect". useEffect(() => { let cancelled = false; auth .linkedProviders() .then(({ providers }) => { if (!cancelled) setLinkedProviders(new Set(providers)); }) .catch(() => { if (!cancelled) setLinkedProviders(new Set()); }); return () => { cancelled = true; }; }, []); function startOAuth(provider: "facebook" | "google") { // /link/start — links the provider to the currently-authenticated // user (auth cookie travels on the top-level navigation). The login // page still uses /start for sign-in / sign-up. const u = new URL(`${BFF_BASE}/api/v1/auth/oauth/${provider}/link/start`); u.searchParams.set("next", RETURN_TO); return u.toString(); } function providerAction(provider: "google" | "facebook") { if (linkedProviders?.has(provider)) { return ; } return ( Connect ); } // Three contextual states for the email-related rows: // • user has no email → "Add your email" row only // • user has email, NOT verified → "Verify your email" row + // "Change your password" row // • user has email, verified → "Change your password" row const needsEmail = !user?.email; const needsVerify = !!user?.email && !user.email_verified_at; return ( <> {/* Suspense boundary required because LinkResult reads useSearchParams — Next.js bails out of SSR otherwise. */} setPwModalOpen(false)} /> setEmailModalOpen(false)} /> ); } function VerifyEmailRow({ email }: { email: string }) { const [busy, setBusy] = useState(false); const [sent, setSent] = useState(false); const [error, setError] = useState(null); async function onResend() { setError(null); setBusy(true); try { await auth.resendVerification(); setSent(true); } catch (err) { setError( err instanceof AuthError ? err.message : "Could not send verification email.", ); } finally { setBusy(false); } } return ( Sent ) : ( ) } /> ); } function LinkResult() { const params = useSearchParams(); const router = useRouter(); const raw = params.get("link"); // Strip the param from the URL so a refresh doesn't re-show the banner. useEffect(() => { if (!raw) return; const next = new URLSearchParams(params.toString()); next.delete("link"); const q = next.toString(); router.replace(q ? `${RETURN_TO}?${q}` : RETURN_TO); }, [raw, params, router]); if (!raw) return null; // Format: ":" — `google:ok`, `facebook:claimed`, etc. const [provider, status] = raw.split(":", 2); const name = provider.charAt(0).toUpperCase() + provider.slice(1); if (status === "ok") { return (

Connected your {name} account.

); } if (status === "claimed") { return (

That {name} account is already linked to another Prabhupāda Says account.

); } return (

Couldn't connect your {name} account. Please try again.

); } function ConnectedPill() { return ( Connected ); } function GoogleGlyph() { return ( ); } function FacebookGlyph() { return ( ); }