Regression fix: restarting onboarding in desktop app required two attempts (#3240)
* Fixed onboarding modal issue, revealed race * Remove logs * Make common reset onboarding code path
This commit is contained in:
7
src-tauri/Cargo.lock
generated
7
src-tauri/Cargo.lock
generated
@ -2626,6 +2626,7 @@ dependencies = [
|
|||||||
"tower-lsp",
|
"tower-lsp",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"url",
|
"url",
|
||||||
|
"urlencoding",
|
||||||
"uuid",
|
"uuid",
|
||||||
"validator",
|
"validator",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@ -6258,6 +6259,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urlpattern"
|
name = "urlpattern"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { useLocation, useNavigate } from 'react-router-dom'
|
|||||||
import { createAndOpenNewProject } from 'lib/tauriFS'
|
import { createAndOpenNewProject } from 'lib/tauriFS'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
|
import { useLspContext } from './LspProvider'
|
||||||
|
|
||||||
const HelpMenuDivider = () => (
|
const HelpMenuDivider = () => (
|
||||||
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
|
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
|
||||||
@ -13,6 +14,7 @@ const HelpMenuDivider = () => (
|
|||||||
|
|
||||||
export function HelpMenu(props: React.PropsWithChildren) {
|
export function HelpMenu(props: React.PropsWithChildren) {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const { onProjectOpen } = useLspContext()
|
||||||
const filePath = useAbsoluteFilePath()
|
const filePath = useAbsoluteFilePath()
|
||||||
const isInProject = location.pathname.includes(paths.FILE)
|
const isInProject = location.pathname.includes(paths.FILE)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -106,9 +108,9 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (isInProject) {
|
if (isInProject) {
|
||||||
navigate('onboarding')
|
navigate(filePath + paths.ONBOARDING.INDEX)
|
||||||
} else {
|
} else {
|
||||||
createAndOpenNewProject(navigate)
|
createAndOpenNewProject({ onProjectOpen, navigate })
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
faBugSlash,
|
faBugSlash,
|
||||||
faCode,
|
faCode,
|
||||||
faCodeCommit,
|
faCodeCommit,
|
||||||
faExclamationCircle,
|
|
||||||
faSquareRootVariable,
|
faSquareRootVariable,
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import { KclEditorMenu } from 'components/ModelingSidebar/ModelingPanes/KclEditorMenu'
|
import { KclEditorMenu } from 'components/ModelingSidebar/ModelingPanes/KclEditorMenu'
|
||||||
|
|||||||
@ -19,7 +19,8 @@ import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/tauriFS'
|
|||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||||
import { sep } from '@tauri-apps/api/path'
|
import { sep } from '@tauri-apps/api/path'
|
||||||
import { ForwardedRef, forwardRef } from 'react'
|
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
||||||
|
import { useLspContext } from 'components/LspProvider'
|
||||||
|
|
||||||
interface AllSettingsFieldsProps {
|
interface AllSettingsFieldsProps {
|
||||||
searchParamTab: SettingsLevel
|
searchParamTab: SettingsLevel
|
||||||
@ -33,9 +34,10 @@ export const AllSettingsFields = forwardRef(
|
|||||||
) => {
|
) => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { onProjectOpen } = useLspContext()
|
||||||
const dotDotSlash = useDotDotSlash()
|
const dotDotSlash = useDotDotSlash()
|
||||||
const {
|
const {
|
||||||
settings: { send, context },
|
settings: { send, context, state },
|
||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
|
|
||||||
const projectPath =
|
const projectPath =
|
||||||
@ -48,19 +50,37 @@ export const AllSettingsFields = forwardRef(
|
|||||||
)
|
)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
function restartOnboarding() {
|
async function restartOnboarding() {
|
||||||
send({
|
send({
|
||||||
type: `set.app.onboardingStatus`,
|
type: `set.app.onboardingStatus`,
|
||||||
data: { level: 'user', value: '' },
|
data: { level: 'user', value: '' },
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isFileSettings) {
|
|
||||||
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
|
||||||
} else {
|
|
||||||
createAndOpenNewProject(navigate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "listener" for the XState to return to "idle" state
|
||||||
|
* when the user resets the onboarding, using the callback above
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
async function navigateToOnboardingStart() {
|
||||||
|
if (
|
||||||
|
state.context.app.onboardingStatus.user === '' &&
|
||||||
|
state.matches('idle')
|
||||||
|
) {
|
||||||
|
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 createAndOpenNewProject({ onProjectOpen, navigate })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigateToOnboardingStart()
|
||||||
|
}, [isFileSettings, navigate, state])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative overflow-y-auto">
|
<div className="relative overflow-y-auto">
|
||||||
<div ref={scrollRef} className="flex flex-col gap-4 px-2">
|
<div ref={scrollRef} className="flex flex-col gap-4 px-2">
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
listProjects,
|
listProjects,
|
||||||
readAppSettingsFile,
|
readAppSettingsFile,
|
||||||
} from './tauri'
|
} from './tauri'
|
||||||
|
import { engineCommandManager } from './singletons'
|
||||||
|
|
||||||
export const isHidden = (fileOrDir: FileEntry) =>
|
export const isHidden = (fileOrDir: FileEntry) =>
|
||||||
!!fileOrDir.name?.startsWith('.')
|
!!fileOrDir.name?.startsWith('.')
|
||||||
@ -116,9 +117,23 @@ export async function getSettingsFolderPaths(projectPath?: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createAndOpenNewProject(
|
export async function createAndOpenNewProject({
|
||||||
|
onProjectOpen,
|
||||||
|
navigate,
|
||||||
|
}: {
|
||||||
|
onProjectOpen: (
|
||||||
|
project: {
|
||||||
|
name: string | null
|
||||||
|
path: string | null
|
||||||
|
} | null,
|
||||||
|
file: FileEntry | null
|
||||||
|
) => void
|
||||||
navigate: (path: string) => void
|
navigate: (path: string) => void
|
||||||
) {
|
}) {
|
||||||
|
// Clear the scene and end the session.
|
||||||
|
engineCommandManager.endSession()
|
||||||
|
|
||||||
|
// Create a new project with the onboarding project name
|
||||||
const configuration = await readAppSettingsFile()
|
const configuration = await readAppSettingsFile()
|
||||||
const projects = await listProjects(configuration)
|
const projects = await listProjects(configuration)
|
||||||
const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects)
|
const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects)
|
||||||
@ -126,6 +141,24 @@ export async function createAndOpenNewProject(
|
|||||||
ONBOARDING_PROJECT_NAME,
|
ONBOARDING_PROJECT_NAME,
|
||||||
nextIndex
|
nextIndex
|
||||||
)
|
)
|
||||||
const newFile = await createNewProjectDirectory(name, bracket, configuration)
|
const newProject = await createNewProjectDirectory(
|
||||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
name,
|
||||||
|
bracket,
|
||||||
|
configuration
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prep the LSP and navigate to the onboarding start
|
||||||
|
onProjectOpen(
|
||||||
|
{
|
||||||
|
name: newProject.name,
|
||||||
|
path: newProject.path,
|
||||||
|
},
|
||||||
|
null
|
||||||
|
)
|
||||||
|
navigate(
|
||||||
|
`${paths.FILE}/${encodeURIComponent(newProject.default_file)}${
|
||||||
|
paths.ONBOARDING.INDEX
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
return newProject
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,22 +3,16 @@ import { onboardingPaths } from 'routes/Onboarding/paths'
|
|||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { Themes, getSystemTheme } from 'lib/theme'
|
import { Themes, getSystemTheme } from 'lib/theme'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import {
|
import { createAndOpenNewProject } from 'lib/tauriFS'
|
||||||
getNextProjectIndex,
|
|
||||||
interpolateProjectNameWithIndex,
|
|
||||||
} from 'lib/tauriFS'
|
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||||
import { paths } from 'lib/paths'
|
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
import { join } from '@tauri-apps/api/path'
|
import { APP_NAME } from 'lib/constants'
|
||||||
import {
|
|
||||||
APP_NAME,
|
|
||||||
ONBOARDING_PROJECT_NAME,
|
|
||||||
PROJECT_ENTRYPOINT,
|
|
||||||
} from 'lib/constants'
|
|
||||||
import { createNewProjectDirectory, listProjects } from 'lib/tauri'
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { useLspContext } from 'components/LspProvider'
|
||||||
|
import { IndexLoaderData } from 'lib/types'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
|
import { useFileContext } from 'hooks/useFileContext'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show either a welcome screen or a warning screen
|
* Show either a welcome screen or a warning screen
|
||||||
@ -47,30 +41,28 @@ function OnboardingResetWarning(props: OnboardingResetWarningProps) {
|
|||||||
{!isTauri() ? (
|
{!isTauri() ? (
|
||||||
<OnboardingWarningWeb {...props} />
|
<OnboardingWarningWeb {...props} />
|
||||||
) : (
|
) : (
|
||||||
<OnboardingWarningDesktop />
|
<OnboardingWarningDesktop {...props} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function OnboardingWarningDesktop() {
|
function OnboardingWarningDesktop(props: OnboardingResetWarningProps) {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const dismiss = useDismiss()
|
const dismiss = useDismiss()
|
||||||
|
const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||||
|
const { context: fileContext } = useFileContext()
|
||||||
|
const { onProjectClose, onProjectOpen } = useLspContext()
|
||||||
|
|
||||||
async function createAndOpenNewProject() {
|
async function onAccept() {
|
||||||
const projects = await listProjects()
|
onProjectClose(
|
||||||
const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects)
|
loaderData.file || null,
|
||||||
const name = interpolateProjectNameWithIndex(
|
fileContext.project.path || null,
|
||||||
ONBOARDING_PROJECT_NAME,
|
false
|
||||||
nextIndex
|
|
||||||
)
|
|
||||||
const newFile = await createNewProjectDirectory(name, bracket)
|
|
||||||
navigate(
|
|
||||||
`${paths.FILE}/${encodeURIComponent(
|
|
||||||
await join(newFile.path, PROJECT_ENTRYPOINT)
|
|
||||||
)}${paths.ONBOARDING.INDEX}`
|
|
||||||
)
|
)
|
||||||
|
await createAndOpenNewProject({ onProjectOpen, navigate })
|
||||||
|
props.setShouldShowWarning(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -88,11 +80,7 @@ function OnboardingWarningDesktop() {
|
|||||||
<OnboardingButtons
|
<OnboardingButtons
|
||||||
className="mt-6"
|
className="mt-6"
|
||||||
dismiss={dismiss}
|
dismiss={dismiss}
|
||||||
next={() => {
|
next={onAccept}
|
||||||
void createAndOpenNewProject()
|
|
||||||
codeManager.updateCodeEditor(bracket)
|
|
||||||
dismiss()
|
|
||||||
}}
|
|
||||||
nextText="Make a new project"
|
nextText="Make a new project"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -79,7 +79,7 @@ export const onboardingRoutes = [
|
|||||||
|
|
||||||
export function useDemoCode() {
|
export function useDemoCode() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!editorManager.editorView) return
|
if (!editorManager.editorView || codeManager.code === bracket) return
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
codeManager.updateCodeStateEditor(bracket)
|
codeManager.updateCodeStateEditor(bracket)
|
||||||
kclManager.isFirstRender = true
|
kclManager.isFirstRender = true
|
||||||
|
|||||||
Reference in New Issue
Block a user