Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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<string | null>(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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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(() => <DashboardSkeleton />, []);

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 }));
};
Expand All @@ -97,6 +155,10 @@ export default function HomePage() {

<div className="w-full flex-1 max-w-6xl mx-auto px-4 py-10 space-y-6">
<CompareForm
username1={username1}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like some new props were added to the CompareForm call, but the component itself wasn't updated to receive them. This is breaking the build right now.

Could you sync the component definition and double-check that it loads okay in live mode?

username2={username2}
setUsername1={setUsername1}
setUsername2={setUsername2}
onSubmit={handleCompare}
loading={loading}
reset={reset}
Expand Down Expand Up @@ -126,3 +188,11 @@ export default function HomePage() {
</main>
);
}

export default function HomePage() {
return (
<Suspense fallback={<DashboardSkeleton />}>
<HomePageInner />
</Suspense>
);
}
17 changes: 9 additions & 8 deletions components/compare-form.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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;
Expand All @@ -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<HTMLInputElement>(null);

// Auto-focus first input on page load
Expand All @@ -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();
};

Expand Down
Loading