From 6675fa8d1ef477cb3a51f6429833f4939516f600 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Tue, 19 Sep 2023 14:06:56 -0400 Subject: [PATCH] UX Papercuts 3: use absolute paths, add error page with buttons to help refresh, etc (#615) * Fix #593: don't prevent default on link click * Use absolute/explicit path for settings Trying to test fix for #594 * Broken: replace almost all relative URLs with absolute * Clean up to use clean useDismiss with absolute path * Merge branch 'main' into franknoirot/ux-papercuts-3a * Add buttons to home, reload, clear, and bug report on error screen --- src/Router.tsx | 2 +- src/components/ErrorPage.tsx | 48 +++++++++++++++++++- src/routes/Onboarding/Camera.tsx | 4 +- src/routes/Onboarding/CmdK.tsx | 4 +- src/routes/Onboarding/CodeEditor.tsx | 4 +- src/routes/Onboarding/Export.tsx | 4 +- src/routes/Onboarding/FutureWork.tsx | 6 +-- src/routes/Onboarding/InteractiveNumbers.tsx | 4 +- src/routes/Onboarding/Introduction.tsx | 23 ++++------ src/routes/Onboarding/ParametricModeling.tsx | 4 +- src/routes/Onboarding/ProjectMenu.tsx | 4 +- src/routes/Onboarding/Sketching.tsx | 4 +- src/routes/Onboarding/Streaming.tsx | 4 +- src/routes/Onboarding/Units.tsx | 4 +- src/routes/Onboarding/UserMenu.tsx | 4 +- src/routes/Onboarding/index.tsx | 41 +++++++++-------- 16 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/Router.tsx b/src/Router.tsx index 0352df41f..6b28c2630 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -130,6 +130,7 @@ const router = createBrowserRouter( path: paths.INDEX, loader: () => isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'), + errorElement: , }, { path: paths.FILE + '/:id', @@ -140,7 +141,6 @@ const router = createBrowserRouter( {!isTauri() && import.meta.env.PROD && } ), - errorElement: , id: paths.FILE, loader: async ({ request, diff --git a/src/components/ErrorPage.tsx b/src/components/ErrorPage.tsx index 2165a5e0c..d6576db9d 100644 --- a/src/components/ErrorPage.tsx +++ b/src/components/ErrorPage.tsx @@ -1,4 +1,12 @@ -import { useRouteError } from 'react-router-dom' +import { isTauri } from 'lib/isTauri' +import { useRouteError, isRouteErrorResponse } from 'react-router-dom' +import { ActionButton } from './ActionButton' +import { + faBug, + faHome, + faRefresh, + faTrash, +} from '@fortawesome/free-solid-svg-icons' export const ErrorPage = () => { let error = useRouteError() @@ -11,7 +19,43 @@ export const ErrorPage = () => {

An unexpected error occurred

-

{String(error)}

+ {isRouteErrorResponse(error) && ( +

+ {error.status}: {error.data} +

+ )} +
+ {isTauri() && ( + + Go Home + + )} + window.location.reload()} + > + Reload + + { + window.localStorage.clear() + }} + > + Clear storage + + + Report Bug + +
) diff --git a/src/routes/Onboarding/Camera.tsx b/src/routes/Onboarding/Camera.tsx index 38ad967b1..61d15ca85 100644 --- a/src/routes/Onboarding/Camera.tsx +++ b/src/routes/Onboarding/Camera.tsx @@ -9,7 +9,6 @@ import { cameraMouseDragGuards, cameraSystems, } from 'lib/cameraControls' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function Units() { const { buttonDownInStream } = useStore((s) => ({ @@ -25,7 +24,6 @@ export default function Units() { }, }, } = useGlobalStateContext() - const dotDotSlash = useDotDotSlash() return (
@@ -74,7 +72,7 @@ export default function Units() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/CmdK.tsx b/src/routes/Onboarding/CmdK.tsx index 859cf8e0a..ca67f3e05 100644 --- a/src/routes/Onboarding/CmdK.tsx +++ b/src/routes/Onboarding/CmdK.tsx @@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons' import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from '../../useStore' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function CmdK() { const { buttonDownInStream } = useStore((s) => ({ @@ -10,7 +9,6 @@ export default function CmdK() { })) const dismiss = useDismiss() const next = useNextClick(onboardingPaths.USER_MENU) - const dotDotSlash = useDotDotSlash() return (
@@ -43,7 +41,7 @@ export default function CmdK() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/CodeEditor.tsx b/src/routes/Onboarding/CodeEditor.tsx index 603e288da..f80e2a96e 100644 --- a/src/routes/Onboarding/CodeEditor.tsx +++ b/src/routes/Onboarding/CodeEditor.tsx @@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from '../../useStore' import { useBackdropHighlight } from 'hooks/useBackdropHighlight' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function CodeEditor() { const { buttonDownInStream } = useStore((s) => ({ @@ -11,7 +10,6 @@ export default function CodeEditor() { })) const dismiss = useDismiss() const next = useNextClick(onboardingPaths.PARAMETRIC_MODELING) - const dotDotSlash = useDotDotSlash() return (
@@ -62,7 +60,7 @@ export default function CodeEditor() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/Export.tsx b/src/routes/Onboarding/Export.tsx index 06ef2b99f..824bf4529 100644 --- a/src/routes/Onboarding/Export.tsx +++ b/src/routes/Onboarding/Export.tsx @@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons' import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from '../../useStore' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function Export() { const { buttonDownInStream } = useStore((s) => ({ @@ -10,7 +9,6 @@ export default function Export() { })) const dismiss = useDismiss() const next = useNextClick(onboardingPaths.SKETCHING) - const dotDotSlash = useDotDotSlash() return (
@@ -42,7 +40,7 @@ export default function Export() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/FutureWork.tsx b/src/routes/Onboarding/FutureWork.tsx index cfeb46f6e..162bab625 100644 --- a/src/routes/Onboarding/FutureWork.tsx +++ b/src/routes/Onboarding/FutureWork.tsx @@ -4,14 +4,12 @@ import { useDismiss } from '.' import { useEffect } from 'react' import { useStore } from 'useStore' import { bracket } from 'lib/exampleKcl' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function FutureWork() { const dismiss = useDismiss() const { deferredSetCode } = useStore((s) => ({ deferredSetCode: s.deferredSetCode, })) - const dotDotSlash = useDotDotSlash() useEffect(() => { deferredSetCode(bracket) @@ -36,7 +34,7 @@ export default function FutureWork() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', @@ -49,7 +47,7 @@ export default function FutureWork() { dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faArrowRight }} > Finish diff --git a/src/routes/Onboarding/InteractiveNumbers.tsx b/src/routes/Onboarding/InteractiveNumbers.tsx index e7503cd81..58450c819 100644 --- a/src/routes/Onboarding/InteractiveNumbers.tsx +++ b/src/routes/Onboarding/InteractiveNumbers.tsx @@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from '../../useStore' import { useBackdropHighlight } from 'hooks/useBackdropHighlight' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function InteractiveNumbers() { const { buttonDownInStream } = useStore((s) => ({ @@ -11,7 +10,6 @@ export default function InteractiveNumbers() { })) const dismiss = useDismiss() const next = useNextClick(onboardingPaths.COMMAND_K) - const dotDotSlash = useDotDotSlash() return (
@@ -102,7 +100,7 @@ export default function InteractiveNumbers() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/Introduction.tsx b/src/routes/Onboarding/Introduction.tsx index d8a61e5b0..3328f5ed0 100644 --- a/src/routes/Onboarding/Introduction.tsx +++ b/src/routes/Onboarding/Introduction.tsx @@ -15,15 +15,13 @@ import { isTauri } from 'lib/isTauri' import { useNavigate } from 'react-router-dom' import { paths } from 'Router' import { useEffect } from 'react' -import { useDotDotSlash } from 'hooks/useDotDotSlash' function OnboardingWithNewFile() { const navigate = useNavigate() - const dotDotSlash = useDotDotSlash() const dismiss = useDismiss() const next = useNextClick(onboardingPaths.INDEX) - const { setCode } = useStore((s) => ({ - setCode: s.setCode, + const { deferredSetCode } = useStore((s) => ({ + deferredSetCode: s.deferredSetCode, })) const { settings: { @@ -53,7 +51,7 @@ function OnboardingWithNewFile() {
dismiss(dotDotSlash())} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', @@ -67,7 +65,7 @@ function OnboardingWithNewFile() { { - setCode(bracket) + deferredSetCode(bracket) next() }} icon={{ icon: faArrowRight }} @@ -91,7 +89,7 @@ function OnboardingWithNewFile() {
dismiss(dotDotSlash())} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', @@ -118,9 +116,9 @@ function OnboardingWithNewFile() { } export default function Introduction() { - const { setCode, code } = useStore((s) => ({ + const { deferredSetCode, code } = useStore((s) => ({ code: s.code, - setCode: s.setCode, + deferredSetCode: s.deferredSetCode, })) const { settings: { @@ -136,11 +134,10 @@ export default function Introduction() { : '' const dismiss = useDismiss() const next = useNextClick(onboardingPaths.CAMERA) - const dotDotSlash = useDotDotSlash() useEffect(() => { - if (code === '') setCode(bracket) - }, [code, setCode]) + if (code === '') deferredSetCode(bracket) + }, [code, deferredSetCode]) return !(code !== '' && code !== bracket) ? (
@@ -180,7 +177,7 @@ export default function Introduction() {
dismiss(dotDotSlash())} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/ParametricModeling.tsx b/src/routes/Onboarding/ParametricModeling.tsx index 7e1d4b812..d0ed6e267 100644 --- a/src/routes/Onboarding/ParametricModeling.tsx +++ b/src/routes/Onboarding/ParametricModeling.tsx @@ -5,7 +5,6 @@ import { useStore } from '../../useStore' import { useBackdropHighlight } from 'hooks/useBackdropHighlight' import { Themes, getSystemTheme } from 'lib/theme' import { useGlobalStateContext } from 'hooks/useGlobalStateContext' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function ParametricModeling() { const { buttonDownInStream } = useStore((s) => ({ @@ -23,7 +22,6 @@ export default function ParametricModeling() { : '' const dismiss = useDismiss() const next = useNextClick(onboardingPaths.INTERACTIVE_NUMBERS) - const dotDotSlash = useDotDotSlash() return (
@@ -62,7 +60,7 @@ export default function ParametricModeling() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/ProjectMenu.tsx b/src/routes/Onboarding/ProjectMenu.tsx index 287c3bb9e..4ce130e77 100644 --- a/src/routes/Onboarding/ProjectMenu.tsx +++ b/src/routes/Onboarding/ProjectMenu.tsx @@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from '../../useStore' import { isTauri } from 'lib/isTauri' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function ProjectMenu() { const { buttonDownInStream } = useStore((s) => ({ @@ -11,7 +10,6 @@ export default function ProjectMenu() { })) const dismiss = useDismiss() const next = useNextClick(onboardingPaths.EXPORT) - const dotDotSlash = useDotDotSlash() return (
@@ -33,7 +31,7 @@ export default function ProjectMenu() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/Sketching.tsx b/src/routes/Onboarding/Sketching.tsx index c5309d607..25ae7e263 100644 --- a/src/routes/Onboarding/Sketching.tsx +++ b/src/routes/Onboarding/Sketching.tsx @@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from 'useStore' import { useEffect } from 'react' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function Sketching() { const { deferredSetCode, buttonDownInStream } = useStore((s) => ({ @@ -16,7 +15,6 @@ export default function Sketching() { useEffect(() => { deferredSetCode('') }, [deferredSetCode]) - const dotDotSlash = useDotDotSlash() return (
@@ -40,7 +38,7 @@ export default function Sketching() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/Streaming.tsx b/src/routes/Onboarding/Streaming.tsx index fbfe5e5a2..4d4e39edd 100644 --- a/src/routes/Onboarding/Streaming.tsx +++ b/src/routes/Onboarding/Streaming.tsx @@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons' import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from '../../useStore' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function Streaming() { const { buttonDownInStream } = useStore((s) => ({ @@ -10,7 +9,6 @@ export default function Streaming() { })) const dismiss = useDismiss() const next = useNextClick(onboardingPaths.EDITOR) - const dotDotSlash = useDotDotSlash() return (
@@ -43,7 +41,7 @@ export default function Streaming() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/Units.tsx b/src/routes/Onboarding/Units.tsx index 511798061..2686d70de 100644 --- a/src/routes/Onboarding/Units.tsx +++ b/src/routes/Onboarding/Units.tsx @@ -6,7 +6,6 @@ import { Toggle } from '../../components/Toggle/Toggle' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { UnitSystem } from 'machines/settingsMachine' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function Units() { const dismiss = useDismiss() @@ -17,7 +16,6 @@ export default function Units() { context: { unitSystem, baseUnit }, }, } = useGlobalStateContext() - const dotDotSlash = useDotDotSlash() return (
@@ -68,7 +66,7 @@ export default function Units() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/UserMenu.tsx b/src/routes/Onboarding/UserMenu.tsx index 32dbb9d02..611e52e1c 100644 --- a/src/routes/Onboarding/UserMenu.tsx +++ b/src/routes/Onboarding/UserMenu.tsx @@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons' import { ActionButton } from '../../components/ActionButton' import { onboardingPaths, useDismiss, useNextClick } from '.' import { useStore } from '../../useStore' -import { useDotDotSlash } from 'hooks/useDotDotSlash' export default function UserMenu() { const { buttonDownInStream } = useStore((s) => ({ @@ -10,7 +9,6 @@ export default function UserMenu() { })) const dismiss = useDismiss() const next = useNextClick(onboardingPaths.PROJECT_MENU) - const dotDotSlash = useDotDotSlash() return (
@@ -30,7 +28,7 @@ export default function UserMenu() {
dismiss(dotDotSlash(2))} + onClick={dismiss} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/index.tsx b/src/routes/Onboarding/index.tsx index b053a1502..dc8c460e2 100644 --- a/src/routes/Onboarding/index.tsx +++ b/src/routes/Onboarding/index.tsx @@ -1,5 +1,5 @@ import { useHotkeys } from 'react-hotkeys-hook' -import { Outlet, useLocation, useNavigate } from 'react-router-dom' +import { Outlet, useRouteLoaderData, useNavigate } from 'react-router-dom' import Introduction from './Introduction' import Camera from './Camera' import Sketching from './Sketching' @@ -15,6 +15,7 @@ import UserMenu from './UserMenu' import ProjectMenu from './ProjectMenu' import Export from './Export' import FutureWork from './FutureWork' +import { IndexLoaderData, paths } from 'Router' export const onboardingPaths = { INDEX: '/', @@ -89,42 +90,44 @@ export function useNextClick(newStatus: string) { settings: { send }, } = useGlobalStateContext() const navigate = useNavigate() - const location = useLocation() - const lastSlashIndex = location.pathname.lastIndexOf('/') + const { project } = useRouteLoaderData(paths.FILE) as IndexLoaderData return useCallback(() => { send({ type: 'Set Onboarding Status', data: { onboardingStatus: newStatus }, }) - navigate(location.pathname.slice(0, lastSlashIndex) + newStatus) - }, [location, lastSlashIndex, newStatus, send, navigate]) + navigate( + paths.FILE + + '/' + + encodeURIComponent(project?.path || 'new') + + paths.ONBOARDING.INDEX.slice(0, -1) + + newStatus + ) + }, [project, newStatus, send, navigate]) } export function useDismiss() { + const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData const { settings: { send }, } = useGlobalStateContext() const navigate = useNavigate() - return useCallback( - (path: string) => { - send({ - type: 'Set Onboarding Status', - data: { onboardingStatus: 'dismissed' }, - }) - console.log('yoyo', window.location.pathname, path) - navigate(path) - }, - [send, navigate] - ) + return useCallback(() => { + send({ + type: 'Set Onboarding Status', + data: { onboardingStatus: 'dismissed' }, + }) + navigate( + paths.FILE + '/' + encodeURIComponent(routeData?.project?.path || 'new') + ) + }, [send, navigate, routeData]) } const Onboarding = () => { - const location = useLocation() const dismiss = useDismiss() - const lastSlashIndex = location.pathname.lastIndexOf('/') - useHotkeys('esc', () => dismiss(location.pathname.slice(0, lastSlashIndex))) + useHotkeys('esc', dismiss) return ( <>