Revert "Make onboarding optional, able to be ignored on desktop" (#6610)
Revert "Make onboarding optional, able to be ignored on desktop (#6564)"
This reverts commit 820082d7f2.
This commit is contained in:
@ -854,14 +854,6 @@ const CustomIconMap = {
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
play: (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M14.2842 9.83887L14.2617 10.6758L7.76172 14.6758L7 14.25V5.75L7.78418 5.33887L14.2842 9.83887ZM8 13.3555L13.0859 10.2246L8 6.70312V13.3555Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
rotate: (
|
||||
<svg
|
||||
viewBox="0 0 20 20"
|
||||
|
||||
@ -1,46 +1,49 @@
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { type NavigateFunction, useLocation } from 'react-router-dom'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { CustomIcon } from '@src/components/CustomIcon'
|
||||
import { useLspContext } from '@src/components/LspProvider'
|
||||
import Tooltip from '@src/components/Tooltip'
|
||||
import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath'
|
||||
import { useMenuListener } from '@src/hooks/useMenu'
|
||||
import { createAndOpenNewTutorialProject } from '@src/lib/desktopFS'
|
||||
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
||||
import { PATHS } from '@src/lib/paths'
|
||||
import { codeManager, kclManager } from '@src/lib/singletons'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { settingsActor } from '@src/lib/singletons'
|
||||
import type { WebContentSendPayload } from '@src/menu/channels'
|
||||
import {
|
||||
acceptOnboarding,
|
||||
catchOnboardingWarnError,
|
||||
} from '@src/routes/Onboarding/utils'
|
||||
import { ONBOARDING_SUBPATHS } from '@src/lib/onboardingPaths'
|
||||
|
||||
const HelpMenuDivider = () => (
|
||||
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
|
||||
)
|
||||
|
||||
export function HelpMenu({
|
||||
navigate = () => {},
|
||||
}: {
|
||||
navigate?: NavigateFunction
|
||||
}) {
|
||||
export function HelpMenu(props: React.PropsWithChildren) {
|
||||
const location = useLocation()
|
||||
const { onProjectOpen } = useLspContext()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const isInProject = location.pathname.includes(PATHS.FILE)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const resetOnboardingWorkflow = () => {
|
||||
const props = {
|
||||
onboardingStatus: ONBOARDING_SUBPATHS.INDEX,
|
||||
navigate,
|
||||
codeManager,
|
||||
kclManager,
|
||||
settingsActor.send({
|
||||
type: 'set.app.onboardingStatus',
|
||||
data: {
|
||||
value: '',
|
||||
level: 'user',
|
||||
},
|
||||
})
|
||||
if (isInProject) {
|
||||
navigate(filePath + PATHS.ONBOARDING.INDEX)
|
||||
} else {
|
||||
createAndOpenNewTutorialProject({
|
||||
onProjectOpen,
|
||||
navigate,
|
||||
}).catch(reportRejection)
|
||||
}
|
||||
acceptOnboarding(props).catch((reason) =>
|
||||
catchOnboardingWarnError(reason, props)
|
||||
)
|
||||
}
|
||||
|
||||
const cb = (data: WebContentSendPayload) => {
|
||||
if (data.menuLabel === 'Help.Replay onboarding tutorial') {
|
||||
if (data.menuLabel === 'Help.Reset onboarding') {
|
||||
resetOnboardingWorkflow()
|
||||
}
|
||||
}
|
||||
@ -65,81 +68,71 @@ export function HelpMenu({
|
||||
as="ul"
|
||||
className="absolute right-0 left-auto flex flex-col w-64 gap-1 p-0 py-2 m-0 mb-1 text-sm border border-solid rounded shadow-lg bottom-full align-stretch text-chalkboard-10 dark:text-inherit bg-chalkboard-110 dark:bg-chalkboard-100 border-chalkboard-110 dark:border-chalkboard-80"
|
||||
>
|
||||
{({ close }) => (
|
||||
<>
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://github.com/KittyCAD/modeling-app/issues/new/choose"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Report a bug
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://github.com/KittyCAD/modeling-app/discussions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Request a feature
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://discord.gg/JQEpHR7Nt2"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Ask the community
|
||||
</HelpMenuItem>
|
||||
<HelpMenuDivider />
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://zoo.dev/docs/kcl-samples"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
KCL code samples
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://zoo.dev/docs/kcl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
KCL docs
|
||||
</HelpMenuItem>
|
||||
<HelpMenuDivider />
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://github.com/KittyCAD/modeling-app/releases"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Release notes
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="button"
|
||||
onClick={() => {
|
||||
const targetPath = location.pathname.includes(PATHS.FILE)
|
||||
? filePath + PATHS.SETTINGS_KEYBINDINGS
|
||||
: PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS
|
||||
navigate(targetPath)
|
||||
}}
|
||||
data-testid="keybindings-button"
|
||||
>
|
||||
Keyboard shortcuts
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="button"
|
||||
onClick={() => {
|
||||
close()
|
||||
resetOnboardingWorkflow()
|
||||
}}
|
||||
>
|
||||
Replay onboarding tutorial
|
||||
</HelpMenuItem>
|
||||
</>
|
||||
)}
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://github.com/KittyCAD/modeling-app/issues/new/choose"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Report a bug
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://github.com/KittyCAD/modeling-app/discussions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Request a feature
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://discord.gg/JQEpHR7Nt2"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Ask the community
|
||||
</HelpMenuItem>
|
||||
<HelpMenuDivider />
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://zoo.dev/docs/kcl-samples"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
KCL code samples
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://zoo.dev/docs/kcl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
KCL docs
|
||||
</HelpMenuItem>
|
||||
<HelpMenuDivider />
|
||||
<HelpMenuItem
|
||||
as="a"
|
||||
href="https://github.com/KittyCAD/modeling-app/releases"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Release notes
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem
|
||||
as="button"
|
||||
onClick={() => {
|
||||
const targetPath = location.pathname.includes(PATHS.FILE)
|
||||
? filePath + PATHS.SETTINGS_KEYBINDINGS
|
||||
: PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS
|
||||
navigate(targetPath)
|
||||
}}
|
||||
data-testid="keybindings-button"
|
||||
>
|
||||
Keyboard shortcuts
|
||||
</HelpMenuItem>
|
||||
<HelpMenuItem as="button" onClick={resetOnboardingWorkflow}>
|
||||
Reset onboarding
|
||||
</HelpMenuItem>
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Link, type NavigateFunction, useLocation } from 'react-router-dom'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
|
||||
import { CustomIcon } from '@src/components/CustomIcon'
|
||||
import { HelpMenu } from '@src/components/HelpMenu'
|
||||
import { NetworkHealthIndicator } from '@src/components/NetworkHealthIndicator'
|
||||
@ -11,10 +12,8 @@ import { APP_VERSION, getReleaseUrl } from '@src/routes/utils'
|
||||
|
||||
export function LowerRightControls({
|
||||
children,
|
||||
navigate = () => {},
|
||||
}: {
|
||||
children?: React.ReactNode
|
||||
navigate?: NavigateFunction
|
||||
}) {
|
||||
const location = useLocation()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
@ -73,7 +72,7 @@ export function LowerRightControls({
|
||||
{!location.pathname.startsWith(PATHS.HOME) && (
|
||||
<NetworkHealthIndicator />
|
||||
)}
|
||||
<HelpMenu navigate={navigate} />
|
||||
<HelpMenu />
|
||||
</menu>
|
||||
</section>
|
||||
)
|
||||
|
||||
@ -6,7 +6,7 @@ import { ActionIcon } from '@src/components/ActionIcon'
|
||||
import type { CustomIconName } from '@src/components/CustomIcon'
|
||||
import Tooltip from '@src/components/Tooltip'
|
||||
import { useSettings } from '@src/lib/singletons'
|
||||
import { ONBOARDING_SUBPATHS } from '@src/lib/onboardingPaths'
|
||||
import { onboardingPaths } from '@src/routes/Onboarding/paths'
|
||||
|
||||
import styles from './ModelingPane.module.css'
|
||||
|
||||
@ -71,7 +71,7 @@ export const ModelingPane = ({
|
||||
const settings = useSettings()
|
||||
const onboardingStatus = settings.app.onboardingStatus
|
||||
const pointerEventsCssClass =
|
||||
onboardingStatus.current === ONBOARDING_SUBPATHS.CAMERA
|
||||
onboardingStatus.current === onboardingPaths.CAMERA
|
||||
? 'pointer-events-none '
|
||||
: 'pointer-events-auto '
|
||||
return (
|
||||
|
||||
@ -24,7 +24,7 @@ import { SIDEBAR_BUTTON_SUFFIX } from '@src/lib/constants'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { useSettings } from '@src/lib/singletons'
|
||||
import { commandBarActor } from '@src/lib/singletons'
|
||||
import { ONBOARDING_SUBPATHS } from '@src/lib/onboardingPaths'
|
||||
import { onboardingPaths } from '@src/routes/Onboarding/paths'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { refreshPage } from '@src/lib/utils'
|
||||
import { hotkeyDisplay } from '@src/lib/hotkeyWrapper'
|
||||
@ -53,7 +53,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
const onboardingStatus = settings.app.onboardingStatus
|
||||
const { send, context } = useModelingContext()
|
||||
const pointerEventsCssClass =
|
||||
onboardingStatus.current === ONBOARDING_SUBPATHS.CAMERA ||
|
||||
onboardingStatus.current === onboardingPaths.CAMERA ||
|
||||
context.store?.openPanes.length === 0
|
||||
? 'pointer-events-none '
|
||||
: 'pointer-events-auto '
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import { useFileSystemWatcher } from '@src/hooks/useFileSystemWatcher'
|
||||
import {
|
||||
PATHS,
|
||||
joinRouterPaths,
|
||||
joinOSPaths,
|
||||
safeEncodeForRouterPaths,
|
||||
} from '@src/lib/paths'
|
||||
import { PATHS } from '@src/lib/paths'
|
||||
import { systemIOActor, useSettings, useToken } from '@src/lib/singletons'
|
||||
import {
|
||||
useHasListedProjects,
|
||||
@ -40,14 +35,14 @@ export function SystemIOMachineLogicListenerDesktop() {
|
||||
if (!requestedProjectName.name) {
|
||||
return
|
||||
}
|
||||
const projectPathWithoutSpecificKCLFile = joinOSPaths(
|
||||
projectDirectoryPath,
|
||||
let projectPathWithoutSpecificKCLFile =
|
||||
projectDirectoryPath +
|
||||
window.electron.path.sep +
|
||||
requestedProjectName.name
|
||||
)
|
||||
const requestedPath = joinRouterPaths(
|
||||
PATHS.FILE,
|
||||
safeEncodeForRouterPaths(projectPathWithoutSpecificKCLFile)
|
||||
)
|
||||
|
||||
const requestedPath = `${PATHS.FILE}/${encodeURIComponent(
|
||||
projectPathWithoutSpecificKCLFile
|
||||
)}`
|
||||
navigate(requestedPath)
|
||||
}, [requestedProjectName])
|
||||
}
|
||||
@ -57,16 +52,12 @@ export function SystemIOMachineLogicListenerDesktop() {
|
||||
if (!requestedFileName.file || !requestedFileName.project) {
|
||||
return
|
||||
}
|
||||
const filePath = joinOSPaths(
|
||||
const projectPath = window.electron.join(
|
||||
projectDirectoryPath,
|
||||
requestedFileName.project,
|
||||
requestedFileName.file
|
||||
)
|
||||
const requestedPath = joinRouterPaths(
|
||||
PATHS.FILE,
|
||||
safeEncodeForRouterPaths(filePath),
|
||||
requestedFileName.subRoute || ''
|
||||
requestedFileName.project
|
||||
)
|
||||
const filePath = window.electron.join(projectPath, requestedFileName.file)
|
||||
const requestedPath = `${PATHS.FILE}/${encodeURIComponent(filePath)}`
|
||||
navigate(requestedPath)
|
||||
}, [requestedFileName])
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ import {
|
||||
useRouteLoaderData,
|
||||
} from 'react-router-dom'
|
||||
|
||||
import type { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus'
|
||||
|
||||
import { useAuthNavigation } from '@src/hooks/useAuthNavigation'
|
||||
import { useFileSystemWatcher } from '@src/hooks/useFileSystemWatcher'
|
||||
import { getAppSettingsFilePath } from '@src/lib/desktop'
|
||||
@ -16,7 +18,7 @@ import { markOnce } from '@src/lib/performance'
|
||||
import { loadAndValidateSettings } from '@src/lib/settings/settingsUtils'
|
||||
import { trap } from '@src/lib/trap'
|
||||
import type { IndexLoaderData } from '@src/lib/types'
|
||||
import { settingsActor } from '@src/lib/singletons'
|
||||
import { settingsActor, useSettings } from '@src/lib/singletons'
|
||||
|
||||
export const RouteProviderContext = createContext({})
|
||||
|
||||
@ -30,6 +32,7 @@ export function RouteProvider({ children }: { children: ReactNode }) {
|
||||
const navigation = useNavigation()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const settings = useSettings()
|
||||
|
||||
useEffect(() => {
|
||||
// On initialization, the react-router-dom does not send a 'loading' state event.
|
||||
@ -43,9 +46,35 @@ export function RouteProvider({ children }: { children: ReactNode }) {
|
||||
markOnce('code/willLoadHome')
|
||||
} else if (isFile) {
|
||||
markOnce('code/willLoadFile')
|
||||
|
||||
/**
|
||||
* TODO: Move to XState. This block has been moved from routerLoaders
|
||||
* and is borrowing the `isFile` logic from the rest of this
|
||||
* telemetry-focused `useEffect`. Once `appMachine` knows about
|
||||
* the current route and navigation, this can be moved into settingsMachine
|
||||
* to fire as soon as the user settings have been read.
|
||||
*/
|
||||
const onboardingStatus: OnboardingStatus =
|
||||
settings.app.onboardingStatus.current || ''
|
||||
// '' is the initial state, 'completed' and 'dismissed' are the final states
|
||||
const needsToOnboard =
|
||||
onboardingStatus.length === 0 ||
|
||||
!(onboardingStatus === 'completed' || onboardingStatus === 'dismissed')
|
||||
const shouldRedirectToOnboarding = isFile && needsToOnboard
|
||||
|
||||
if (
|
||||
shouldRedirectToOnboarding &&
|
||||
settingsActor.getSnapshot().matches('idle')
|
||||
) {
|
||||
navigate(
|
||||
(first ? location.pathname : navigation.location?.pathname) +
|
||||
PATHS.ONBOARDING.INDEX +
|
||||
onboardingStatus.slice(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
setFirstState(false)
|
||||
}, [first, navigation, location.pathname])
|
||||
}, [navigation])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDesktop()) return
|
||||
|
||||
@ -6,12 +6,16 @@ import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Fragment } from 'react/jsx-runtime'
|
||||
|
||||
import { ActionButton } from '@src/components/ActionButton'
|
||||
import { useLspContext } from '@src/components/LspProvider'
|
||||
import { SettingsFieldInput } from '@src/components/Settings/SettingsFieldInput'
|
||||
import { SettingsSection } from '@src/components/Settings/SettingsSection'
|
||||
import { getSettingsFolderPaths } from '@src/lib/desktopFS'
|
||||
import { useDotDotSlash } from '@src/hooks/useDotDotSlash'
|
||||
import {
|
||||
createAndOpenNewTutorialProject,
|
||||
getSettingsFolderPaths,
|
||||
} from '@src/lib/desktopFS'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
||||
import { ONBOARDING_SUBPATHS } from '@src/lib/onboardingPaths'
|
||||
import { PATHS } from '@src/lib/paths'
|
||||
import type { Setting } from '@src/lib/settings/initialSettings'
|
||||
import type {
|
||||
@ -24,17 +28,9 @@ import {
|
||||
} from '@src/lib/settings/settingsUtils'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { toSync } from '@src/lib/utils'
|
||||
import {
|
||||
codeManager,
|
||||
kclManager,
|
||||
settingsActor,
|
||||
useSettings,
|
||||
} from '@src/lib/singletons'
|
||||
import { settingsActor, useSettings } from '@src/lib/singletons'
|
||||
import { APP_VERSION, IS_NIGHTLY, getReleaseUrl } from '@src/routes/utils'
|
||||
import {
|
||||
acceptOnboarding,
|
||||
catchOnboardingWarnError,
|
||||
} from '@src/routes/Onboarding/utils'
|
||||
import { waitFor } from 'xstate'
|
||||
|
||||
interface AllSettingsFieldsProps {
|
||||
searchParamTab: SettingsLevel
|
||||
@ -48,6 +44,8 @@ export const AllSettingsFields = forwardRef(
|
||||
) => {
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const { onProjectOpen } = useLspContext()
|
||||
const dotDotSlash = useDotDotSlash()
|
||||
const context = useSettings()
|
||||
|
||||
const projectPath = useMemo(() => {
|
||||
@ -65,18 +63,26 @@ export const AllSettingsFields = forwardRef(
|
||||
: undefined
|
||||
|
||||
return projectPath
|
||||
}, [location.pathname, isFileSettings])
|
||||
}, [location.pathname])
|
||||
|
||||
async function restartOnboarding() {
|
||||
const props = {
|
||||
onboardingStatus: ONBOARDING_SUBPATHS.INDEX,
|
||||
navigate,
|
||||
codeManager,
|
||||
kclManager,
|
||||
settingsActor.send({
|
||||
type: `set.app.onboardingStatus`,
|
||||
data: { level: 'user', value: '' },
|
||||
})
|
||||
await waitFor(settingsActor, (s) => s.matches('idle'), {
|
||||
timeout: 10_000,
|
||||
}).catch(reportRejection)
|
||||
|
||||
if (isFileSettings) {
|
||||
// If we're in a project, first navigate to the onboarding start here
|
||||
// so we can trigger the warning screen if necessary
|
||||
navigate(dotDotSlash(1) + PATHS.ONBOARDING.INDEX)
|
||||
} else {
|
||||
// If we're in the global settings, create a new project and navigate
|
||||
// to the onboarding start in that project
|
||||
await createAndOpenNewTutorialProject({ onProjectOpen, navigate })
|
||||
}
|
||||
acceptOnboarding(props).catch((reason) =>
|
||||
catchOnboardingWarnError(reason, props)
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user