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:
Frank Noirot
2024-08-02 15:38:39 -04:00
committed by GitHub
parent 9b594efe53
commit 9dcc955760
7 changed files with 98 additions and 49 deletions

7
src-tauri/Cargo.lock generated
View File

@ -2626,6 +2626,7 @@ dependencies = [
"tower-lsp",
"ts-rs",
"url",
"urlencoding",
"uuid",
"validator",
"wasm-bindgen",
@ -6258,6 +6259,12 @@ dependencies = [
"serde",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "urlpattern"
version = "0.2.0"

View File

@ -6,6 +6,7 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { createAndOpenNewProject } from 'lib/tauriFS'
import { paths } from 'lib/paths'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { useLspContext } from './LspProvider'
const HelpMenuDivider = () => (
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
@ -13,6 +14,7 @@ const HelpMenuDivider = () => (
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()
@ -106,9 +108,9 @@ export function HelpMenu(props: React.PropsWithChildren) {
},
})
if (isInProject) {
navigate('onboarding')
navigate(filePath + paths.ONBOARDING.INDEX)
} else {
createAndOpenNewProject(navigate)
createAndOpenNewProject({ onProjectOpen, navigate })
}
}}
>

View File

@ -3,7 +3,6 @@ import {
faBugSlash,
faCode,
faCodeCommit,
faExclamationCircle,
faSquareRootVariable,
} from '@fortawesome/free-solid-svg-icons'
import { KclEditorMenu } from 'components/ModelingSidebar/ModelingPanes/KclEditorMenu'

View File

@ -19,7 +19,8 @@ import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/tauriFS'
import { paths } from 'lib/paths'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
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 {
searchParamTab: SettingsLevel
@ -33,9 +34,10 @@ export const AllSettingsFields = forwardRef(
) => {
const location = useLocation()
const navigate = useNavigate()
const { onProjectOpen } = useLspContext()
const dotDotSlash = useDotDotSlash()
const {
settings: { send, context },
settings: { send, context, state },
} = useSettingsAuthContext()
const projectPath =
@ -48,19 +50,37 @@ export const AllSettingsFields = forwardRef(
)
: undefined
function restartOnboarding() {
async function restartOnboarding() {
send({
type: `set.app.onboardingStatus`,
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 (
<div className="relative overflow-y-auto">
<div ref={scrollRef} className="flex flex-col gap-4 px-2">

View File

@ -14,6 +14,7 @@ import {
listProjects,
readAppSettingsFile,
} from './tauri'
import { engineCommandManager } from './singletons'
export const isHidden = (fileOrDir: FileEntry) =>
!!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
) {
}) {
// Clear the scene and end the session.
engineCommandManager.endSession()
// Create a new project with the onboarding project name
const configuration = await readAppSettingsFile()
const projects = await listProjects(configuration)
const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects)
@ -126,6 +141,24 @@ export async function createAndOpenNewProject(
ONBOARDING_PROJECT_NAME,
nextIndex
)
const newFile = await createNewProjectDirectory(name, bracket, configuration)
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
const newProject = await createNewProjectDirectory(
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
}

View File

@ -3,22 +3,16 @@ import { onboardingPaths } from 'routes/Onboarding/paths'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Themes, getSystemTheme } from 'lib/theme'
import { bracket } from 'lib/exampleKcl'
import {
getNextProjectIndex,
interpolateProjectNameWithIndex,
} from 'lib/tauriFS'
import { createAndOpenNewProject } from 'lib/tauriFS'
import { isTauri } from 'lib/isTauri'
import { useNavigate } from 'react-router-dom'
import { paths } from 'lib/paths'
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
import { codeManager, kclManager } from 'lib/singletons'
import { join } from '@tauri-apps/api/path'
import {
APP_NAME,
ONBOARDING_PROJECT_NAME,
PROJECT_ENTRYPOINT,
} from 'lib/constants'
import { createNewProjectDirectory, listProjects } from 'lib/tauri'
import { APP_NAME } from 'lib/constants'
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
@ -47,30 +41,28 @@ function OnboardingResetWarning(props: OnboardingResetWarningProps) {
{!isTauri() ? (
<OnboardingWarningWeb {...props} />
) : (
<OnboardingWarningDesktop />
<OnboardingWarningDesktop {...props} />
)}
</div>
</div>
)
}
function OnboardingWarningDesktop() {
function OnboardingWarningDesktop(props: OnboardingResetWarningProps) {
const navigate = useNavigate()
const dismiss = useDismiss()
const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData
const { context: fileContext } = useFileContext()
const { onProjectClose, onProjectOpen } = useLspContext()
async function createAndOpenNewProject() {
const projects = await listProjects()
const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects)
const name = interpolateProjectNameWithIndex(
ONBOARDING_PROJECT_NAME,
nextIndex
)
const newFile = await createNewProjectDirectory(name, bracket)
navigate(
`${paths.FILE}/${encodeURIComponent(
await join(newFile.path, PROJECT_ENTRYPOINT)
)}${paths.ONBOARDING.INDEX}`
async function onAccept() {
onProjectClose(
loaderData.file || null,
fileContext.project.path || null,
false
)
await createAndOpenNewProject({ onProjectOpen, navigate })
props.setShouldShowWarning(false)
}
return (
@ -88,11 +80,7 @@ function OnboardingWarningDesktop() {
<OnboardingButtons
className="mt-6"
dismiss={dismiss}
next={() => {
void createAndOpenNewProject()
codeManager.updateCodeEditor(bracket)
dismiss()
}}
next={onAccept}
nextText="Make a new project"
/>
</>

View File

@ -79,7 +79,7 @@ export const onboardingRoutes = [
export function useDemoCode() {
useEffect(() => {
if (!editorManager.editorView) return
if (!editorManager.editorView || codeManager.code === bracket) return
setTimeout(async () => {
codeManager.updateCodeStateEditor(bracket)
kclManager.isFirstRender = true