()
| 14 | import { useSelector } from "../state/shared/hooks"; |
| 15 | import { AppColours, WHITE } from "../styles/colours"; |
| 16 | import { FileHandlerContext } from "./context"; |
| 17 | |
| 18 | export const MIN_WIDTH_FOR_APPLICATION = 1200; |
| 19 | |
| 20 | export const TopHatTutorial: React.FC = () => { |
| 21 | const open = useUserData((user) => user.tutorial); |
| 22 | const storage = useSelector((state) => state.app.storage); |
| 23 | const running = isAppRunning(storage); |
| 24 | |
| 25 | // While the tutorial is up, a file dropped anywhere is a backup to import rather than a statement |
| 26 | const { isDragActive, openFileDialog, setFileImportHandler } = useContext(FileHandlerContext); |
| 27 | const jsonImportStatus = useSelector((state) => state.app.jsonImport); |
| 28 | useEffect(() => { |
| 29 | if (!open || !running) return; |
| 30 | |
| 31 | setFileImportHandler("JSON-IMPORT"); |
| 32 | return () => { |
| 33 | setFileImportHandler("GENERAL-IMPORT"); |
| 34 | resetJSONImportStatus(); |
| 35 | }; |
| 36 | }, [open, running, setFileImportHandler]); |
| 37 | |
| 38 | const [page, setPage] = useState<"welcome" | "import">("welcome"); |
| 39 | useEffect(() => { |
| 40 | if (open) setPage("welcome"); |
| 41 | }, [open]); |
| 42 | const showWelcome = useCallback(() => { |
| 43 | setPage("welcome"); |
| 44 | resetJSONImportStatus(); |
| 45 | }, []); |
| 46 | const showImport = useCallback(() => setPage("import"), []); |
| 47 | |
| 48 | const [width, setWidth] = useState(9001); |
| 49 | useEffect(() => { |
| 50 | const observer = new ResizeObserver((entries) => setWidth(entries[0].contentRect.width)); |
| 51 | observer.observe(document.body); |
| 52 | return () => observer.disconnect(); |
| 53 | }, [setWidth]); |
| 54 | const [widthDismissed, setWidthDismissed] = useState(false); |
| 55 | const dismissWidth = useCallback(() => setWidthDismissed(true), []); |
| 56 | |
| 57 | // The store starts in the tutorial state, and is left in it when boot or saved data fails. None of |
| 58 | // that is an invitation, so the tutorial is only offered once the app itself can be shown. |
| 59 | if (!running) return null; |
| 60 | |
| 61 | // The narrow-window warning is shown to everyone, not only while the tutorial is open |
| 62 | if (width < MIN_WIDTH_FOR_APPLICATION && !widthDismissed) |
| 63 | return ( |
| 64 | <Dialog open={true} maxWidth="md" fullWidth={true}> |
| 65 | <TutorialNarrowWindowContents onContinue={dismissWidth} /> |
| 66 | </Dialog> |
| 67 | ); |
| 68 | |
| 69 | return ( |
| 70 | <Dialog |
| 71 | open={open} |
| 72 | maxWidth="md" |
| 73 | fullWidth={true} |
nothing calls this directly
no test coverage detected