From 86b4a8ab356e98071217debf16d3ba643d72f600 Mon Sep 17 00:00:00 2001 From: Leander Furumo Date: Sun, 22 Jun 2025 02:09:23 +0200 Subject: [PATCH] show loading state on guess submission --- src/app/components/GuessForm.tsx | 17 +++++++----- src/app/components/HomeClient.tsx | 43 +++++++++++++++++++------------ src/app/translations.ts | 2 ++ 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/app/components/GuessForm.tsx b/src/app/components/GuessForm.tsx index 6189f52..4b65f00 100644 --- a/src/app/components/GuessForm.tsx +++ b/src/app/components/GuessForm.tsx @@ -7,9 +7,10 @@ interface GuessFormProps { onInputChange: (e: React.ChangeEvent) => void; onSubmit: (e: React.FormEvent) => void; disabled?: boolean; + loading?: boolean; } -const GuessForm: React.FC = ({ input, onInputChange, onSubmit, disabled }) => { +const GuessForm: React.FC = ({ input, onInputChange, onSubmit, disabled, loading }) => { const { language } = useLanguage(); const t = translations[language]; return ( @@ -19,21 +20,23 @@ const GuessForm: React.FC = ({ input, onInputChange, onSubmit, d value={input} onChange={onInputChange} className={`focus:border-accent-primary h-12 w-60 rounded border px-2 py-1 focus:outline-none md:w-75 ${ - disabled + disabled || loading ? "cursor-not-allowed border-gray-500 bg-gray-400 opacity-50" : "bg-background-secondary duration-300" }`} - placeholder={t.placeholder} - disabled={disabled} + placeholder={loading ? t.loading || "Loading..." : t.placeholder} + disabled={disabled || loading} /> ); diff --git a/src/app/components/HomeClient.tsx b/src/app/components/HomeClient.tsx index 0ab6bd2..7dbe499 100644 --- a/src/app/components/HomeClient.tsx +++ b/src/app/components/HomeClient.tsx @@ -24,6 +24,7 @@ export default function HomeClient({ session }: { session: Session }) { const [matches, setMatches] = useState([]); const [timer, setTimer] = useState(0); const [timerStarted, setTimerStarted] = useState(false); + const [loading, setLoading] = useState(false); const timerRef = useRef(null); const [howToPlayOpen, setHowToPlayOpen] = useState(false); const [repeatMessage, setRepeatMessage] = useState(""); @@ -77,24 +78,33 @@ export default function HomeClient({ session }: { session: Session }) { return; } setRepeatMessage(""); - const res = await fetch("/api/guess", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ input: trimmedInput }), - }); - if (res.ok) { - const data = await res.json(); - if (data.name && matches.some((m) => m.name && m.name.toLowerCase() === data.name.toLowerCase())) { - setRepeatMessage(t.alreadySubmitted); - setInput(""); - return; - } - setMatches((prev) => [...prev, { input: trimmedInput, ...data }]); - if (data.correct && !timerStarted) { - startTimer(); + setLoading(true); + + try { + const res = await fetch("/api/guess", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ input: trimmedInput }), + }); + if (res.ok) { + const data = await res.json(); + if (data.name && matches.some((m) => m.name && m.name.toLowerCase() === data.name.toLowerCase())) { + setRepeatMessage(t.alreadySubmitted); + setInput(""); + return; + } + setMatches((prev) => [...prev, { input: trimmedInput, ...data }]); + if (data.correct && !timerStarted) { + startTimer(); + } } + } catch (error) { + console.error("Error submitting guess:", error); + setRepeatMessage("An error occurred. Please try again."); + } finally { + setLoading(false); + setInput(""); } - setInput(""); }; return ( @@ -136,6 +146,7 @@ export default function HomeClient({ session }: { session: Session }) { }} onSubmit={handleSubmit} disabled={correctMatchesCount === GOAL_AMOUNT} + loading={loading} /> {repeatMessage &&
{repeatMessage}
} diff --git a/src/app/translations.ts b/src/app/translations.ts index 7df4979..63a4eca 100644 --- a/src/app/translations.ts +++ b/src/app/translations.ts @@ -4,6 +4,7 @@ export const translations = { alreadySubmitted: "You have already entered that name!", placeholder: "Enter the username of a member...", submit: "Submit", + loading: "Loading...", howToPlayTitle: "How to Play", howToPlay1: "Any Discord username or nickname who has the paying member role should be accepted.", howToPlay2: "No repeats allowed.", @@ -24,6 +25,7 @@ export const translations = { alreadySubmitted: "Du har allerede skrevet det navnet!", placeholder: "Skriv brukernavnet til et medlem...", submit: "Send", + loading: "Laster...", howToPlayTitle: "Slik spiller du", howToPlay1: "Alle bør Discordbrukernavn eller kallenavn som har betalende medlem rollen godtas.", howToPlay2: "Du kan ikke gjenta navn.",