Fix 'Retry missed ones' showing locked, already-answered items
The retry reused the same React keys for the same activity ids, so every activity component kept its internal state (selected option, feedback, flip, matched pairs) from the first run — the retried items appeared pre-answered with the old wrong answer and could not be answered again. - StudyFeed now keeps a runId that increments on every restart/retry and includes it in the slide keys, so each run gets fresh component state. - Scroll snaps back to the first slide instantly on a new run (the old requestAnimationFrame + smooth goTo could race the queue change). New retry suite (14 checks): answer wrong → results → retry → items are unanswered and enabled → answer correctly → 2/2 on the retry run, plus the full 'study again' reset. Deck-chrome 15/15, pwa 16/16, final-ux 12/12, security 8/8, contrast 20/20.
This commit is contained in:
parent
9e4ecd3ee0
commit
1bacf43ed2
1 changed files with 12 additions and 3 deletions
|
|
@ -24,6 +24,8 @@ export function StudyFeed({ deck }: { deck: Deck }) {
|
|||
const [streak, setStreak] = useState(0);
|
||||
const [bestStreak, setBestStreak] = useState(0);
|
||||
const [retryMode, setRetryMode] = useState(false);
|
||||
/** Bumped on every restart so the activity components remount fresh. */
|
||||
const [runId, setRunId] = useState(0);
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const recordedRef = useRef(false);
|
||||
|
|
@ -134,12 +136,19 @@ export function StudyFeed({ deck }: { deck: Deck }) {
|
|||
setStreak(0);
|
||||
setRetryMode(retry);
|
||||
recordedRef.current = retry; // don't overwrite the best score with a retry run
|
||||
// A new run: remount every activity (fresh state) and jump to the top.
|
||||
setRunId((value) => value + 1);
|
||||
setIndex(0);
|
||||
requestAnimationFrame(() => goTo(0));
|
||||
},
|
||||
[goTo],
|
||||
[],
|
||||
);
|
||||
|
||||
// Whenever a run starts (including the first render), snap back to slide 1.
|
||||
useEffect(() => {
|
||||
const container = scrollRef.current;
|
||||
if (container) container.scrollTop = 0;
|
||||
}, [runId]);
|
||||
|
||||
const progressValue = Math.min(index + 1, slideCount);
|
||||
const progressMax = slideCount;
|
||||
|
||||
|
|
@ -205,7 +214,7 @@ export function StudyFeed({ deck }: { deck: Deck }) {
|
|||
>
|
||||
{queue.map((activity, activityIndex) => (
|
||||
<section
|
||||
key={activity.id}
|
||||
key={`${activity.id}-${runId}`}
|
||||
aria-label={`Activity ${activityIndex + 1} of ${total}`}
|
||||
className="flex h-full snap-start snap-always items-stretch justify-center overflow-y-auto overscroll-y-contain"
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue