Reported: during a loading screen (freezing a deck) the Study deck / Make my homework switch stayed live, so the run could be swapped out mid-flight; a request that finished afterwards could even save a deck the user never saw and navigate them into it. - New lib/wizard-busy.ts: a tiny store both wizards publish to while phase === 'loading' (UploadScreen) or view.kind === 'loading' (HomeworkScreen); WizardModeSwitch hides itself while busy. - Both wizards abort the in-flight request on unmount, so leaving via the header logo or the Back gesture stops the run instead of letting it finish invisibly and save/navigate later. - Deck Cancel keeps its long-standing behaviour (wizard + cancelled notice + Retry), matching the freeze-loading suite. Verified: wizard suite 92 checks (mode switch hidden during deck and homework runs, it returns after Cancel, and a delayed reply after leaving mid-run saves nothing and navigates nowhere) plus mobile 134/134 (also de-flaked the sheet's Escape close by waiting for detach), regression 74/74, declutter 116/116, animation 8/8, native 35/35, i18n 25/25, phase1 38/38, phase2 43/43, phase3 20/20, freeze-loading 6/6. Lint + build clean. SW freezmo-v29.
44 lines
1.5 KiB
TypeScript
44 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
|
|
import { SegmentedPill } from "@/components/SegmentedPill";
|
|
import { useI18n } from "@/lib/i18n";
|
|
import { useIsPhone } from "@/lib/use-phone";
|
|
import { useWizardBusy } from "@/lib/wizard-busy";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
/**
|
|
* In-page switch between the two wizards (New deck ⇄ Make my homework).
|
|
* Phones reach both through this control and the centre tab; desktop keeps
|
|
* the rail rows as well. Hidden while either wizard is generating: leaving a
|
|
* loading run through it would abandon the request mid-flight.
|
|
*/
|
|
export function WizardModeSwitch({ mode }: { mode: "deck" | "homework" }) {
|
|
const { t } = useI18n();
|
|
const isPhone = useIsPhone();
|
|
const busy = useWizardBusy();
|
|
|
|
if (busy) return null;
|
|
|
|
return (
|
|
<div className="segmented mb-5" role="group" aria-label={t("wizard.mode.aria")}>
|
|
<Link
|
|
href="/create"
|
|
aria-current={mode === "deck" ? "page" : undefined}
|
|
className={cn("relative", isPhone && "seg-slide")}
|
|
>
|
|
{isPhone && mode === "deck" ? <SegmentedPill layoutId="wizard-mode" /> : null}
|
|
<span className="seg-label relative z-10">{t("wizard.mode.deck")}</span>
|
|
</Link>
|
|
<Link
|
|
href="/homework"
|
|
aria-current={mode === "homework" ? "page" : undefined}
|
|
className={cn("relative", isPhone && "seg-slide")}
|
|
>
|
|
{isPhone && mode === "homework" ? <SegmentedPill layoutId="wizard-mode" /> : null}
|
|
<span className="seg-label relative z-10">{t("wizard.mode.homework")}</span>
|
|
</Link>
|
|
</div>
|
|
);
|
|
}
|