fix(ui): a reviewed log takes the focus back once, and a dialog's focus survives a Retry
E32 review round 1 (F1, F2). - Issue composer: the review's focus-return token became state in S4 and was never spent, so every Preview → Edit round trip remounted the attachments and moved the focus from the toggle to the log's Review button. The token is cleared once it has been used, as the ref was. - Modal (initialFocus "field"): the late-form watch moved the focus to the first control once and stopped. A failed load's Retry then gave way to the spinner, its focus fell to <body> inside the aria-modal dialog, and the form's first field never got it. The watch now re-arms when the focus falls to <body>: it waits on the ✕ and moves to the first field when the form arrives. Focus in a dialog stacked on top is left alone.
This commit is contained in:
@@ -412,6 +412,7 @@ function ComposerBody({
|
||||
}}
|
||||
onReview={setReviewing}
|
||||
reviewed={reviewed}
|
||||
setReviewed={setReviewed}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -596,6 +597,7 @@ function Attachments({
|
||||
onAdd,
|
||||
onReview,
|
||||
reviewed,
|
||||
setReviewed,
|
||||
}: {
|
||||
session: DebugSession;
|
||||
shotsQ: { data?: { items: DebugScreenshot[] }; isError: boolean; refetch: () => unknown };
|
||||
@@ -605,6 +607,7 @@ function Attachments({
|
||||
onAdd: (files: Iterable<File>) => void;
|
||||
onReview: (source: Source) => void;
|
||||
reviewed: Source | null;
|
||||
setReviewed: (spent: null) => void;
|
||||
}) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const lang = i18n.language;
|
||||
@@ -614,10 +617,12 @@ function Attachments({
|
||||
const pick = useRef<HTMLInputElement>(null);
|
||||
const reviewButtons = useRef<Partial<Record<Source, HTMLButtonElement | null>>>({});
|
||||
// On mount after a review closed (this list unmounts while one is open), that log's Review button
|
||||
// takes the focus back. Only a mount or a new review moves it, so no later render steals it.
|
||||
// takes the focus back — once: the spent token keeps a later remount (back from the preview) off it.
|
||||
useEffect(() => {
|
||||
if (reviewed) reviewButtons.current[reviewed]?.focus();
|
||||
}, [reviewed]);
|
||||
if (!reviewed) return;
|
||||
reviewButtons.current[reviewed]?.focus();
|
||||
setReviewed(null);
|
||||
}, [reviewed, setReviewed]);
|
||||
const remove = useMutation({
|
||||
mutationFn: (shotId: number) => api.removeDebugScreenshot(session.id, shotId),
|
||||
meta: { failTitle: "recorder.composer.removeShotFailed" },
|
||||
|
||||
Reference in New Issue
Block a user