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:
Farzad Yousefzadeh
2024-09-09 19:59:36 +03:00
committed by GitHub
parent 7c2cfba0ac
commit 5f8d4f8294
34 changed files with 3707 additions and 3139 deletions

View File

@ -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',