diff --git a/packages/challenger-webui/src/pages/AnswerChallenge.tsx b/packages/challenger-webui/src/pages/AnswerChallenge.tsx index ae12b7167..4f0646065 100644 --- a/packages/challenger-webui/src/pages/AnswerChallenge.tsx +++ b/packages/challenger-webui/src/pages/AnswerChallenge.tsx @@ -22,7 +22,13 @@ import { useTranslationContext, } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; -import { useEffect, useMemo, useRef, useState } from "preact/hooks"; +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "preact/hooks"; import { revalidateChallengeSession, useChallengeSession, @@ -86,6 +92,17 @@ export function AnswerChallenge({ const [deliveryErrorDetails, setDeliveryErrorDetails] = useState(); const [newCodeSent, setNewCodeSent] = useState(false); const pinInput = useRef(null); + // Keep the ref callback stable so it focuses the PIN only when the input + // appears. An inline callback runs again on every render, and the resend + // countdown re-renders every second, pulling focus back from the other + // actions on this page. + const pinRef = useCallback( + (element: HTMLInputElement | null) => { + pinInput.current = element; + if (focus) doAutoFocus(element); + }, + [focus], + ); const { result, retry, isRefreshing } = useChallengeSession(session); const lastStatus = @@ -381,10 +398,7 @@ export function AnswerChallenge({ {verificationCodeLabel} { - pinInput.current = element; - if (focus) doAutoFocus(element); - }} + ref={pinRef} id="pin" name="pin" type="text"