diff --git a/app/page.tsx b/app/page.tsx index 8504569..45e5e38 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,7 @@ "use client"; -import { useMemo, useState } from "react"; +import { Suspense, useEffect, useEffectEvent, useMemo, useRef, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; import { CompareForm } from "../components/compare-form"; import { ResultDashboard } from "../components/result-dashboard"; import { DashboardSkeleton } from "../components/skeletons"; @@ -16,14 +17,24 @@ type ApiResponse = { error?: string; }; -export default function HomePage() { +function HomePageInner() { const { t } = useTranslation(); + const router = useRouter(); + const searchParams = useSearchParams(); + const initialUsernames = searchParams.getAll("username"); + const initialUsername1 = initialUsernames[0] ?? ""; + const initialUsername2 = initialUsernames[1] ?? ""; const [loading, setLoading] = useState(false); const [error, setError] = useState(null); + const [username1, setUsername1] = useState(initialUsername1); + const [username2, setUsername2] = useState(initialUsername2); const [data, setData] = useState<{ user1: UserResult; user2: UserResult; } | null>(null); + // Track the URL pair we last fetched against so back/forward navigation + // can resync the form and results without re-fetching identical pairs. + const lastFetchedPairRef = useRef<[string, string] | null>(null); const localizeErrorMessage = (message?: string) => { switch (message) { @@ -43,6 +54,11 @@ export default function HomePage() { }; const handleCompare = async (u1: string, u2: string) => { + lastFetchedPairRef.current = [u1, u2]; + router.push( + `/?username=${encodeURIComponent(u1)}&username=${encodeURIComponent(u2)}`, + { scroll: false } + ); setLoading(true); setError(null); setData(null); @@ -79,14 +95,56 @@ export default function HomePage() { } }; + // Resync form + results to whatever the URL says — handles initial mount + // AND back/forward navigation. We fetch only when the URL pair differs from + // the last pair we fetched, so no infinite loop with the router.push above. + const syncToUrl = useEffectEvent((u1: string, u2: string) => { + setUsername1(u1); + setUsername2(u2); + + if (!u1 || !u2) { + // Empty params: clear results so the empty state matches the URL. + lastFetchedPairRef.current = null; + setData(null); + setError(null); + return; + } + + const last = lastFetchedPairRef.current; + if (last && last[0] === u1 && last[1] === u2) { + // URL already reflects the most recent fetch; nothing to do. + return; + } + + void handleCompare(u1, u2); + }); + + useEffect(() => { + const params = searchParams.getAll("username"); + syncToUrl(params[0] ?? "", params[1] ?? ""); + }, [searchParams]); + const skeleton = useMemo(() => , []); const reset = () => { setData(null); setError(null); + setUsername1(""); + setUsername2(""); + router.push("/", { scroll: false }); }; const swapUsers = () => { + const nextUsername1 = username2; + const nextUsername2 = username1; + + setUsername1(nextUsername1); + setUsername2(nextUsername2); + router.push( + `/?username=${encodeURIComponent(nextUsername1)}&username=${encodeURIComponent(nextUsername2)}`, + { scroll: false } + ); + if (!data) return; setData((d) => ({ user1: d!.user2, user2: d!.user1 })); }; @@ -97,6 +155,10 @@ export default function HomePage() {
); } + +export default function HomePage() { + return ( + }> + + + ); +} diff --git a/components/compare-form.tsx b/components/compare-form.tsx index 56c4d5a..350790d 100644 --- a/components/compare-form.tsx +++ b/components/compare-form.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect } from "react"; +import { useRef, useEffect } from "react"; import { Button } from "./ui/button"; import { ArrowLeftRight, RefreshCw } from "lucide-react"; import { @@ -12,6 +12,10 @@ import { Alert, AlertDescription } from "./ui/alert"; import { useTranslation } from "./language-provider"; type CompareFormProps = { + username1: string; + username2: string; + setUsername1: (value: string) => void; + setUsername2: (value: string) => void; data?: boolean; onSubmit: (u1: string, u2: string) => void; loading?: boolean; @@ -21,16 +25,17 @@ type CompareFormProps = { }; export function CompareForm({ + username1, + username2, + setUsername1, + setUsername2, onSubmit, - data, loading, swapUsers, reset, error, }: CompareFormProps) { const { t } = useTranslation(); - const [username1, setUsername1] = useState("pbiggar"); - const [username2, setUsername2] = useState("CoralineAda"); const firstInputRef = useRef(null); // Auto-focus first input on page load @@ -42,14 +47,10 @@ export function CompareForm({ const isEmpty = !username1.trim() && !username2.trim(); const handleSwap = () => { - setUsername1(username2); - setUsername2(username1); if (swapUsers) swapUsers(); }; const handleReset = () => { - setUsername1(""); - setUsername2(""); if (reset) reset(); };