Migrate to XState v5 (#3735)
* migrate settingsMachine
* Guard events with properties instead
* migrate settingsMachine
* Migrate auth machine
* Migrate file machine
* Migrate depracated types
* Migrate home machine
* Migrate command bar machine
* Version fixes
* Migrate command bar machine
* Migrate modeling machine
* Migrate types, state.can, state.matches and state.nextEvents
* Fix syntax
* Pass in modelingState into editor manager instead of modeling event
* Fix issue with missing command bar provider
* Fix state transition
* Fix type issue in Home
* Make sure no guards rely on event type
* Fix up command bar submission logic
* Home machine tweaks to get things running
* Fix AST fillet function args
* Handle "Set selection" when it is called by actor onDone
* Remove unused imports
* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)
* Fix injectin project to the fileTree machine
* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)"
This reverts commit 4b43ff69d1
.
* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)
* Re-run CI
* Restore success toasts on file/folder deletion
* Replace casting with guarding against event.type
* Remove console.log
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Replace all instances of event casting with guards against event.type
---------
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
This commit is contained in:
committed by
GitHub
parent
7c2cfba0ac
commit
5f8d4f8294
@ -14,7 +14,7 @@ import { type HomeLoaderData } from 'lib/types'
|
||||
import Loading from 'components/Loading'
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { homeMachine } from '../machines/homeMachine'
|
||||
import { ContextFrom, EventFrom } from 'xstate'
|
||||
import { fromPromise } from 'xstate'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import {
|
||||
getNextSearchParams,
|
||||
@ -68,95 +68,102 @@ const Home = () => {
|
||||
)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [state, send, actor] = useMachine(homeMachine, {
|
||||
context: {
|
||||
projects: loadedProjects,
|
||||
defaultProjectName: settings.projects.defaultProjectName.current,
|
||||
defaultDirectory: settings.app.projectDirectory.current,
|
||||
},
|
||||
actions: {
|
||||
navigateToProject: (
|
||||
context: ContextFrom<typeof homeMachine>,
|
||||
event: EventFrom<typeof homeMachine>
|
||||
) => {
|
||||
if (event.data && 'name' in event.data) {
|
||||
let projectPath =
|
||||
context.defaultDirectory +
|
||||
window.electron.path.sep +
|
||||
event.data.name
|
||||
onProjectOpen(
|
||||
{
|
||||
name: event.data.name,
|
||||
path: projectPath,
|
||||
},
|
||||
null
|
||||
)
|
||||
commandBarSend({ type: 'Close' })
|
||||
navigate(`${PATHS.FILE}/${encodeURIComponent(projectPath)}`)
|
||||
}
|
||||
},
|
||||
toastSuccess: (_, event) => toast.success((event.data || '') + ''),
|
||||
toastError: (_, event) => toast.error((event.data || '') + ''),
|
||||
},
|
||||
services: {
|
||||
readProjects: async (context: ContextFrom<typeof homeMachine>) =>
|
||||
listProjects(),
|
||||
createProject: async (
|
||||
context: ContextFrom<typeof homeMachine>,
|
||||
event: EventFrom<typeof homeMachine, 'Create project'>
|
||||
) => {
|
||||
let name = (
|
||||
event.data && 'name' in event.data
|
||||
? event.data.name
|
||||
: settings.projects.defaultProjectName.current
|
||||
).trim()
|
||||
|
||||
if (doesProjectNameNeedInterpolated(name)) {
|
||||
const nextIndex = getNextProjectIndex(name, projects)
|
||||
name = interpolateProjectNameWithIndex(name, nextIndex)
|
||||
}
|
||||
|
||||
await createNewProjectDirectory(name)
|
||||
|
||||
return `Successfully created "${name}"`
|
||||
},
|
||||
renameProject: async (
|
||||
context: ContextFrom<typeof homeMachine>,
|
||||
event: EventFrom<typeof homeMachine, 'Rename project'>
|
||||
) => {
|
||||
const { oldName, newName } = event.data
|
||||
let name = newName ? newName : context.defaultProjectName
|
||||
if (doesProjectNameNeedInterpolated(name)) {
|
||||
const nextIndex = await getNextProjectIndex(name, projects)
|
||||
name = interpolateProjectNameWithIndex(name, nextIndex)
|
||||
}
|
||||
|
||||
await renameProjectDirectory(
|
||||
window.electron.path.join(context.defaultDirectory, oldName),
|
||||
name
|
||||
)
|
||||
return `Successfully renamed "${oldName}" to "${name}"`
|
||||
},
|
||||
deleteProject: async (
|
||||
context: ContextFrom<typeof homeMachine>,
|
||||
event: EventFrom<typeof homeMachine, 'Delete project'>
|
||||
) => {
|
||||
await window.electron.rm(
|
||||
window.electron.path.join(context.defaultDirectory, event.data.name),
|
||||
{
|
||||
recursive: true,
|
||||
const [state, send, actor] = useMachine(
|
||||
homeMachine.provide({
|
||||
actions: {
|
||||
navigateToProject: ({ context, event }) => {
|
||||
if ('data' in event && event.data && 'name' in event.data) {
|
||||
let projectPath =
|
||||
context.defaultDirectory +
|
||||
window.electron.path.sep +
|
||||
event.data.name
|
||||
onProjectOpen(
|
||||
{
|
||||
name: event.data.name,
|
||||
path: projectPath,
|
||||
},
|
||||
null
|
||||
)
|
||||
commandBarSend({ type: 'Close' })
|
||||
navigate(`${PATHS.FILE}/${encodeURIComponent(projectPath)}`)
|
||||
}
|
||||
)
|
||||
return `Successfully deleted "${event.data.name}"`
|
||||
},
|
||||
toastSuccess: ({ event }) =>
|
||||
toast.success(
|
||||
('data' in event && typeof event.data === 'string' && event.data) ||
|
||||
('output' in event &&
|
||||
typeof event.output === 'string' &&
|
||||
event.output) ||
|
||||
''
|
||||
),
|
||||
toastError: ({ event }) =>
|
||||
toast.error(
|
||||
('data' in event && typeof event.data === 'string' && event.data) ||
|
||||
('output' in event &&
|
||||
typeof event.output === 'string' &&
|
||||
event.output) ||
|
||||
''
|
||||
),
|
||||
},
|
||||
},
|
||||
guards: {
|
||||
'Has at least 1 project': (_, event: EventFrom<typeof homeMachine>) => {
|
||||
if (event.type !== 'done.invoke.read-projects') return false
|
||||
return event?.data?.length ? event.data?.length >= 1 : false
|
||||
actors: {
|
||||
readProjects: fromPromise(() => listProjects()),
|
||||
createProject: fromPromise(async ({ input }) => {
|
||||
let name = (
|
||||
input && 'name' in input && input.name
|
||||
? input.name
|
||||
: settings.projects.defaultProjectName.current
|
||||
).trim()
|
||||
|
||||
if (doesProjectNameNeedInterpolated(name)) {
|
||||
const nextIndex = getNextProjectIndex(name, projects)
|
||||
name = interpolateProjectNameWithIndex(name, nextIndex)
|
||||
}
|
||||
|
||||
await createNewProjectDirectory(name)
|
||||
|
||||
return `Successfully created "${name}"`
|
||||
}),
|
||||
renameProject: fromPromise(async ({ input }) => {
|
||||
const { oldName, newName, defaultProjectName, defaultDirectory } =
|
||||
input
|
||||
let name = newName ? newName : defaultProjectName
|
||||
if (doesProjectNameNeedInterpolated(name)) {
|
||||
const nextIndex = await getNextProjectIndex(name, projects)
|
||||
name = interpolateProjectNameWithIndex(name, nextIndex)
|
||||
}
|
||||
|
||||
await renameProjectDirectory(
|
||||
window.electron.path.join(defaultDirectory, oldName),
|
||||
name
|
||||
)
|
||||
return `Successfully renamed "${oldName}" to "${name}"`
|
||||
}),
|
||||
deleteProject: fromPromise(async ({ input }) => {
|
||||
await window.electron.rm(
|
||||
window.electron.path.join(input.defaultDirectory, input.name),
|
||||
{
|
||||
recursive: true,
|
||||
}
|
||||
)
|
||||
return `Successfully deleted "${input.name}"`
|
||||
}),
|
||||
},
|
||||
},
|
||||
})
|
||||
guards: {
|
||||
'Has at least 1 project': ({ event }) => {
|
||||
if (event.type !== 'xstate.done.actor.read-projects') return false
|
||||
console.log(`from has at least 1 project: ${event.output.length}`)
|
||||
return event.output.length ? event.output.length >= 1 : false
|
||||
},
|
||||
},
|
||||
}),
|
||||
{
|
||||
input: {
|
||||
projects: loadedProjects,
|
||||
defaultProjectName: settings.projects.defaultProjectName.current,
|
||||
defaultDirectory: settings.app.projectDirectory.current,
|
||||
},
|
||||
}
|
||||
)
|
||||
const { projects } = state.context
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const { searchResults, query, setQuery } = useProjectSearch(projects)
|
||||
@ -197,14 +204,18 @@ const Home = () => {
|
||||
)
|
||||
|
||||
if (newProjectName !== project.name) {
|
||||
send('Rename project', {
|
||||
data: { oldName: project.name, newName: newProjectName },
|
||||
send({
|
||||
type: 'Rename project',
|
||||
data: { oldName: project.name, newName: newProjectName as string },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteProject(project: Project) {
|
||||
send('Delete project', { data: { name: project.name || '' } })
|
||||
send({
|
||||
type: 'Delete project',
|
||||
data: { name: project.name || '' },
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@ -217,7 +228,9 @@ const Home = () => {
|
||||
<h1 className="text-3xl font-bold">Your Projects</h1>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() => send('Create project')}
|
||||
onClick={() =>
|
||||
send({ type: 'Create project', data: { name: '' } })
|
||||
}
|
||||
className="group !bg-primary !text-chalkboard-10 !border-primary hover:shadow-inner hover:hue-rotate-15"
|
||||
iconStart={{
|
||||
icon: 'plus',
|
||||
|
Reference in New Issue
Block a user