this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(public-form-runner): prevent duplicate enter navigation

+37 -19
+37 -19
components/public-form-runner.tsx
··· 13 13 useCallback, 14 14 useEffect, 15 15 useMemo, 16 + useRef, 16 17 useState, 17 18 type KeyboardEvent as ReactKeyboardEvent, 18 19 } from "react"; ··· 76 77 ); 77 78 const { t } = useI18n(); 78 79 const [answers, setAnswers] = useState<Record<string, string | string[]>>({}); 79 - const [history, setHistory] = useState<string[]>(initialHistory); 80 - const [cursor, setCursor] = useState(0); 80 + const [route, setRoute] = useState({ history: initialHistory, cursor: 0 }); 81 81 const [toasts, setToasts] = useState<ToastData[]>([]); 82 82 const [isSubmitting, setIsSubmitting] = useState(false); 83 83 const [isComplete, setIsComplete] = useState(false); 84 + const submitLockRef = useRef(false); 84 85 85 86 const blocksById = useMemo( 86 87 () => new Map(form.blocks.map((block) => [block.id, block])), 87 88 [form.blocks], 88 89 ); 90 + const history = route.history; 91 + const maxCursor = Math.max(history.length - 1, 0); 92 + const cursor = Math.min(route.cursor, maxCursor); 89 93 const currentBlockId = history[cursor] ?? initialHistory[0] ?? null; 90 94 const currentBlock = currentBlockId 91 95 ? (blocksById.get(currentBlockId) ?? null) ··· 317 321 return; 318 322 } 319 323 324 + const sourceBlockId = currentBlock.id; 320 325 const resolvedNextBlockId = resolveNextBlockId( 321 326 form.blocks, 322 - currentBlock.id, 327 + sourceBlockId, 323 328 answerSet, 324 329 ); 325 330 326 331 if (!resolvedNextBlockId) { 332 + if (submitLockRef.current) { 333 + return; 334 + } 335 + 336 + submitLockRef.current = true; 337 + 327 338 try { 328 339 setIsSubmitting(true); 329 340 await submitResponse(form.slug, answerSet); ··· 337 348 ); 338 349 } finally { 339 350 setIsSubmitting(false); 351 + submitLockRef.current = false; 340 352 } 341 353 342 354 return; 343 355 } 344 356 345 - setHistory((current) => [ 346 - ...current.slice(0, cursor + 1), 347 - resolvedNextBlockId, 348 - ]); 349 - setCursor((current) => current + 1); 357 + setRoute((current) => { 358 + const activeBlockId = current.history[current.cursor] ?? null; 359 + 360 + if (activeBlockId !== sourceBlockId) { 361 + return current; 362 + } 363 + 364 + const nextHistory = [ 365 + ...current.history.slice(0, current.cursor + 1), 366 + resolvedNextBlockId, 367 + ]; 368 + 369 + return { 370 + history: nextHistory, 371 + cursor: nextHistory.length - 1, 372 + }; 373 + }); 350 374 }, 351 - [ 352 - answers, 353 - currentBlock, 354 - cursor, 355 - form.blocks, 356 - form.slug, 357 - showToast, 358 - t, 359 - validateStep, 360 - ], 375 + [answers, currentBlock, form.blocks, form.slug, showToast, t, validateStep], 361 376 ); 362 377 363 378 const handleBack = useCallback(() => { 364 - setCursor((current) => Math.max(0, current - 1)); 379 + setRoute((current) => ({ 380 + ...current, 381 + cursor: Math.max(0, current.cursor - 1), 382 + })); 365 383 }, []); 366 384 367 385 function handleAdvanceKeyDown(