From ac64b9fa455985862499a816528727dfdaaa1b40 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Sun, 17 Mar 2024 10:36:06 -0400 Subject: [PATCH] Start of a unified appMachine + messy broken migrations of the sub-machines --- package.json | 6 +- src/Auth.tsx | 7 +- src/Router.tsx | 31 +-- src/components/AppMachineProvider.tsx | 21 ++ src/hooks/useStateMachineCommands.ts | 7 +- src/machines/appMachine.ts | 264 ++++++++++++++++++++++++++ src/machines/authMachine.ts | 130 ++++++------- src/machines/homeMachine.ts | 77 ++++---- src/routes/Home.tsx | 7 +- yarn.lock | 23 ++- 10 files changed, 427 insertions(+), 146 deletions(-) create mode 100644 src/components/AppMachineProvider.tsx create mode 100644 src/machines/appMachine.ts diff --git a/package.json b/package.json index 1d542348d..0bbaee05a 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@types/react-dom": "^18.0.0", "@uiw/react-codemirror": "^4.21.20", "@xstate/inspect": "^0.8.0", - "@xstate/react": "^3.2.2", + "@xstate/react": "^4.1.0", "crypto-js": "^4.2.0", "debounce-promise": "^3.1.2", "formik": "^2.4.3", @@ -50,7 +50,7 @@ "three": "^0.160.0", "toml": "^3.0.0", "ts-node": "^10.9.1", - "typescript": "^5.2.2", + "typescript": "^5.4.2", "uuid": "^9.0.1", "vitest": "^1.3.1", "vscode-jsonrpc": "^8.1.0", @@ -58,7 +58,7 @@ "wasm-pack": "^0.12.1", "web-vitals": "^3.5.0", "ws": "^8.13.0", - "xstate": "^4.38.2", + "xstate": "^5.9.1", "zustand": "^4.4.5" }, "scripts": { diff --git a/src/Auth.tsx b/src/Auth.tsx index 19260e605..2665b29fe 100644 --- a/src/Auth.tsx +++ b/src/Auth.tsx @@ -1,12 +1,11 @@ +import { AppMachineContext } from 'machines/appMachine' import Loading from './components/Loading' -import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' // Wrapper around protected routes, used in src/Router.tsx export const Auth = ({ children }: React.PropsWithChildren) => { - const { auth } = useSettingsAuthContext() - const isLoggingIn = auth?.state.matches('checkIfLoggedIn') + const isAuthenticating = AppMachineContext.useSelector(s => s.matches('Loading')) - return isLoggingIn ? ( + return isAuthenticating ? ( Loading Modeling App... diff --git a/src/Router.tsx b/src/Router.tsx index faf638d9e..501c1d3b1 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -16,8 +16,6 @@ import makeUrlPathRelative from './lib/makeUrlPathRelative' import DownloadAppBanner from 'components/DownloadAppBanner' import { WasmErrBanner } from 'components/WasmErrBanner' import { CommandBar } from 'components/CommandBar/CommandBar' -import ModelingMachineProvider from 'components/ModelingMachineProvider' -import FileMachineProvider from 'components/FileMachineProvider' import { paths } from 'lib/paths' import { fileLoader, @@ -25,10 +23,7 @@ import { indexLoader, onboardingRedirectLoader, } from 'lib/routeLoaders' -import { CommandBarProvider } from 'components/CommandBar/CommandBarProvider' -import SettingsAuthProvider from 'components/SettingsAuthProvider' -import LspProvider from 'components/LspProvider' -import { KclContextProvider } from 'lang/KclSingleton' +import { AppMachineProvider } from 'components/AppMachineProvider' export const BROWSER_FILE_NAME = 'new' @@ -37,15 +32,9 @@ const router = createBrowserRouter([ loader: indexLoader, id: paths.INDEX, element: ( - - - - - - - - - + + + ), children: [ { @@ -61,14 +50,10 @@ const router = createBrowserRouter([ id: paths.FILE, element: ( - - - - - - - - + + + + {!isTauri() && import.meta.env.PROD && } ), diff --git a/src/components/AppMachineProvider.tsx b/src/components/AppMachineProvider.tsx new file mode 100644 index 000000000..0e51f846c --- /dev/null +++ b/src/components/AppMachineProvider.tsx @@ -0,0 +1,21 @@ +import { AppMachineContext } from 'machines/appMachine' +import { PropsWithChildren } from 'react' +import { useLocation, useNavigate } from 'react-router-dom' + +export const AppMachineProvider = ({ children }: PropsWithChildren) => { + const navigate = useNavigate() + const location = useLocation() + + return ( + + {children} + + ) +} diff --git a/src/hooks/useStateMachineCommands.ts b/src/hooks/useStateMachineCommands.ts index 23d8d434d..9a6b60959 100644 --- a/src/hooks/useStateMachineCommands.ts +++ b/src/hooks/useStateMachineCommands.ts @@ -13,6 +13,7 @@ import { } from 'components/NetworkHealthIndicator' import { useKclContext } from 'lang/KclSingleton' import { useStore } from 'useStore' +import { AppMachineContext } from 'machines/appMachine' // This might not be necessary, AnyStateMachine from xstate is working export type AllMachines = @@ -46,7 +47,7 @@ export default function useStateMachineCommands< allCommandsRequireNetwork = false, onCancel, }: UseStateMachineCommandsArgs) { - const { commandBarSend } = useCommandsContext() + const commandsActorRef = AppMachineContext.useSelector(s => s.children.commands) const { overallState } = useNetworkStatus() const { isExecuting } = useKclContext() const { isStreamReady } = useStore((s) => ({ @@ -72,10 +73,10 @@ export default function useStateMachineCommands< ) .filter((c) => c !== null) as Command[] // TS isn't smart enough to know this filter removes nulls - commandBarSend({ type: 'Add commands', data: { commands: newCommands } }) + commandsActorRef?.send({ type: 'Add commands', data: { commands: newCommands } }) return () => { - commandBarSend({ + commandsActorRef?.send({ type: 'Remove commands', data: { commands: newCommands }, }) diff --git a/src/machines/appMachine.ts b/src/machines/appMachine.ts new file mode 100644 index 000000000..39ab6550d --- /dev/null +++ b/src/machines/appMachine.ts @@ -0,0 +1,264 @@ +import { ProjectWithEntryPointMetadata } from 'lib/types' +import { assign, fromCallback, fromPromise, setup } from 'xstate' +import { settingsMachine } from './settingsMachine' +import { commandBarMachine } from './commandBarMachine' +import { homeMachine } from './homeMachine' +import { modelingMachine } from './modelingMachine' +import { fileMachine } from './fileMachine' +import { useLocation, useNavigate } from 'react-router-dom' +import { TOKEN_PERSIST_KEY, getUser, persistedToken } from './authMachine' +import { Models } from '@kittycad/lib/dist/types/src' +import { createActorContext } from '@xstate/react' +import { paths } from 'lib/paths' +import { isTauri } from 'lib/isTauri' +import { sep } from '@tauri-apps/api/path' + +export const appMachine = setup({ + types: { + context: {} as { + token?: string + user?: Models['User_type'] + currentProject?: ProjectWithEntryPointMetadata + currentFile?: string + navigate: ReturnType + location: ReturnType + }, + events: {} as + | { type: 'Sign out' } + | { + type: 'Open project' + data: { + project: ProjectWithEntryPointMetadata + file: string + } + } + | { type: 'Close project' } + | { type: 'AuthActor.done'; output: Models['User_type'] }, + input: {} as { + navigate: ReturnType + location: ReturnType + }, + }, + actions: { + 'Clear currentProject and currentFile': assign({ + currentProject: undefined, + currentFile: undefined, + }), + 'Set currentProject': assign({ + currentProject: ({ event, context }) => + event.type === 'Open project' + ? event.data.project + : context.currentProject, + }), + 'Set currentFile': assign({ + currentFile: ({ event, context }) => + event.type === 'Open project' ? event.data.file : context.currentFile, + }), + 'Delete auth state': () => { + localStorage.removeItem(TOKEN_PERSIST_KEY) + assign({ token: undefined }) + }, + 'Go to sign in page': ({ context }) => { + context.navigate(paths.SIGN_IN) + }, + 'Navigate home': ({ context }) => { + context.navigate(paths.INDEX) + }, + 'Navigate to file': ({ context, event }) => { + if (event.type !== 'Open project') return + context.navigate( + paths.FILE + + event.data.project.path + + (isTauri() ? sep : '/') + + event.data.file + ) + }, + 'Assign user': assign({ + user: ({ event, context }) => + event.type === 'AuthActor.done' ? event.output : context.user, + }), + 'Persist auth state': ({ context }) => { + localStorage.setItem(TOKEN_PERSIST_KEY, context.token || '') + }, + }, + actors: { + SettingsActor: settingsMachine, + EngineConnectionActor: fromCallback(({ sendBack, receive, input }) => { + // TODO implement actor + }), + CommandBarMachine: commandBarMachine, + LspActor: fromCallback(({ sendBack, receive }) => { + // TODO implement actor + }), + HomeMachine: homeMachine, + ModelingMachine: modelingMachine, + ClientSideSceneActor: fromCallback(({ sendBack, receive }) => { + // TODO implement actor + }), + ProjectMachine: fileMachine, + KclActor: fromCallback(({ sendBack, receive }) => { + // TODO implement actor + }), + AuthActor: fromPromise( + ({ input }: { input: { token: string | undefined } }) => + getUser(input.token) + ), + }, + schemas: { + events: { + 'Sign out': { + type: 'object', + properties: {}, + }, + 'Open project': { + type: 'object', + properties: {}, + }, + 'Close project': { + type: 'object', + properties: {}, + }, + '': { + type: 'object', + properties: {}, + }, + }, + }, +}).createMachine({ + /** @xstate-layout N4IgpgJg5mDOIC5QEEAOqAEBZAhgYwAsBLAOzADoBlIqMiDUgYmtowHsBXAFwG0AGALqJQqNrCJcibEsJAAPRADYA7AEZyAFgCcG1QGZVygKyKAHMq3KANCACeiAExH1WvUY2Lnejct0a9AL4BNmiYuISkFCx0DCTkABJsALZgjADyqGAkGKgATmwAVmB4vIKyouKS0rIKCNp85A7KTnxqGq3m5jb2CMqKeuSWWqYjfKoeRmNBIejY+MRkVDQxpOQACvlFJeyZJIwAwgA2YmA5m8WlQkggFRJSMte19ZqqRsp6fHqKfIqq-UbdJRmcjKPjOZymVRaMHvaYgUJzCKLAAybBwEFIUEYEGkFFIADc2ABrCg4bgEfhXERiO7VR6IPTecimLRGYbDIymTyqUyAhAORSKcitVw8zx6Bx6ZpwhHhBYUVHozGMMC5fK5cioQ44LgAMzYuSS5DJXApZWutyqD1AtSMDgc5E8fW0ouUfQcfIcP00GhZpl90PeziCwRAJDYEDgsll80i5RpVpqiAAtEZGhpJkY3F8+KZc74+ao+Bphe1-VL-Vm+PaZbM5ZElrRILF45V7km6g7jNoPB49FooaoBXyNA5TMKVHxi99jO5VLWwrHFtFm6tEilW7TrfJENCQe4dIo+wOB3o+YphsLWu0s8NfK4F4j5Y2VnENoULjsspvE-Tev2r3GAV70FFRz3aRpWiPHwtCcBxVHnUMYyRKJlmbTguB-ds-yHBDyCze1OhUe1jE9PN8OcdpvFeBCL0fesUTRDESCgLC6RtRBJkGJoswQ0EuXaaw7EQQdyC+KVQW+b4Pg0EMAiAA */ + context: ({ input }) => ({ + token: persistedToken, + navigate: input.navigate, + location: input.location, + }), + id: 'App Machine', + initial: 'Loading', + states: { + 'Signed in': { + initial: 'Home', + on: { + 'Sign out': { + target: 'Signed out', + actions: [ + { + type: 'Delete auth state', + }, + { + type: 'Go to sign in page', + }, + ], + }, + }, + invoke: [ + { + id: 'settings', + systemId: 'settings', + input: {}, + src: 'SettingsActor', + }, + { + id: 'engine', + systemId: 'engine', + input: {}, + src: 'EngineConnectionActor', + }, + { + id: 'commands', + systemId: 'commands', + input: {}, + src: 'CommandBarMachine', + }, + { + id: 'lsp', + systemId: 'lsp', + input: {}, + src: 'LspActor', + }, + ], + states: { + Home: { + on: { + 'Open project': { + target: 'Project open', + actions: { + type: 'Navigate to file', + }, + }, + }, + entry: { + type: 'Clear currentProject and currentFile', + }, + invoke: { + id: 'home', + input: {}, + src: 'HomeMachine', + }, + }, + 'Project open': { + on: { + 'Close project': { + target: 'Home', + actions: { + type: 'Navigate home', + }, + }, + }, + entry: [ + { + type: 'Set currentProject', + }, + { + type: 'Set currentFile', + }, + ], + invoke: [ + { + id: 'modeling', + systemId: 'modeling', + input: {}, + src: 'ModelingMachine', + }, + { + input: {}, + src: 'ClientSideSceneActor', + }, + { + id: 'project', + systemId: 'project', + input: {}, + src: 'ProjectMachine', + }, + { + input: {}, + src: 'KclActor', + }, + ], + }, + }, + }, + + 'Signed out': { + type: 'final', + }, + + Loading: { + invoke: { + src: 'AuthActor', + id: 'auth', + input: ({ context: { token } }) => ({ token }), + onDone: { + target: 'Signed in', + actions: [ + assign({ + user: ({ event }) => event.output, + }), + 'Persist auth state', + ], + }, + onError: 'Signed out', + }, + }, + }, +}) + +export const AppMachineContext = createActorContext(appMachine) diff --git a/src/machines/authMachine.ts b/src/machines/authMachine.ts index 531e3ef28..88669538e 100644 --- a/src/machines/authMachine.ts +++ b/src/machines/authMachine.ts @@ -1,4 +1,4 @@ -import { createMachine, assign } from 'xstate' +import { createMachine, assign, setup, fromPromise } from 'xstate' import { Models } from '@kittycad/lib' import withBaseURL from '../lib/withBaseURL' import { isTauri } from 'lib/isTauri' @@ -39,86 +39,82 @@ export type Events = } export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY' -const persistedToken = +export const persistedToken = localStorage?.getItem(TOKEN_PERSIST_KEY) || getCookie('__Secure-next-auth.session-token') || - '' + undefined -export const authMachine = createMachine( - { - id: 'Auth', - initial: 'checkIfLoggedIn', - states: { - checkIfLoggedIn: { - id: 'check-if-logged-in', - invoke: { - src: 'getUser', - id: 'check-logged-in', - onDone: [ - { - target: 'loggedIn', - actions: assign({ - user: (context, event) => event.data, - }), - }, - ], - onError: [ - { - target: 'loggedOut', - actions: assign({ - user: () => undefined, - }), - }, - ], - }, - }, - loggedIn: { - entry: ['goToIndexPage'], - on: { - 'Log out': { - target: 'loggedOut', - }, - }, - }, - loggedOut: { - entry: ['goToSignInPage'], - on: { - 'Log in': { - target: 'checkIfLoggedIn', +export const authMachine = setup({ + types: { + events: {} as { type: 'Log out' } | { type: 'Log in' }, + context: {} as UserContext, + }, + actors: { + fetchUser: fromPromise(({ input }: { input: { token: string | undefined }}) => getUser(input.token)), + }, + actions: { + goToIndexPage: () => ({}), + goToSignInPage: () => ({}), + } +}).createMachine({ + id: 'Auth', + preserveActionOrder: true, + initial: 'checkIfLoggedIn', + context: { + token: persistedToken, + }, + states: { + checkIfLoggedIn: { + id: 'check-if-logged-in', + invoke: { + id: 'getUser', + src: 'fetchUser', + input: ({ context: { token } }) => ({ token }), + onDone: [ + { + target: 'loggedIn', actions: assign({ - token: (_, event) => { - const token = event.token || '' - localStorage.setItem(TOKEN_PERSIST_KEY, token) - return token - }, + user: ({ event }) => event.output, }), }, + ], + onError: [ + { + target: 'loggedOut', + actions: assign({ + user: () => undefined, + }), + }, + ], + }, + }, + loggedIn: { + entry: ['goToIndexPage'], + on: { + 'Log out': { + target: 'loggedOut', }, }, }, - schema: { events: {} as { type: 'Log out' } | { type: 'Log in' } }, - predictableActionArguments: true, - preserveActionOrder: true, - context: { - token: persistedToken, + loggedOut: { + entry: { type: 'goToIndexPage' }, + on: { + 'Log in': { + target: 'checkIfLoggedIn', + }, + }, }, }, - { - actions: {}, - services: { getUser }, - guards: {}, - delays: {}, - } -) +}) -async function getUser(context: UserContext) { +export async function getUser(token?: string) { const url = withBaseURL('/user') const headers: { [key: string]: string } = { 'Content-Type': 'application/json', } - if (!context.token && isTauri()) throw new Error('No token found') - if (context.token) headers['Authorization'] = `Bearer ${context.token}` + if (!token && isTauri()) throw new Error('No token found') + if (token) headers['Authorization'] = `Bearer ${token}` if (SKIP_AUTH) return LOCAL_USER const userPromise = !isTauri() @@ -130,7 +126,7 @@ async function getUser(context: UserContext) { .then((res) => res.json()) .catch((err) => console.error('error from Browser getUser', err)) : invoke>('get_user', { - token: context.token, + token: token, hostname: VITE_KC_API_BASE_URL, }).catch((err) => console.error('error from Tauri getUser', err)) @@ -138,10 +134,10 @@ async function getUser(context: UserContext) { if ('error_code' in user) throw new Error(user.message) - return user + return user as Models['User_type'] } -function getCookie(cname: string): string { +export function getCookie(cname: string): string { if (isTauri()) { return '' } diff --git a/src/machines/homeMachine.ts b/src/machines/homeMachine.ts index abb3c1d56..9f53d4773 100644 --- a/src/machines/homeMachine.ts +++ b/src/machines/homeMachine.ts @@ -1,28 +1,54 @@ -import { assign, createMachine } from 'xstate' +import { assign, createMachine, fromPromise, setup } from 'xstate' import { type ProjectWithEntryPointMetadata } from 'lib/types' import { HomeCommandSchema } from 'lib/commandBarConfigs/homeCommandConfig' +import { useNavigate } from 'react-router-dom' -export const homeMachine = createMachine( +export const homeMachine = setup({ + types: { + context: {} as { + projects: ProjectWithEntryPointMetadata[] + defaultProjectName: string + defaultDirectory: string + navigate: ReturnType + }, + events: {} as + | { type: 'Open project'; data: HomeCommandSchema['Open project'] } + | { type: 'Rename project'; data: HomeCommandSchema['Rename project'] } + | { type: 'Create project'; data: HomeCommandSchema['Create project'] } + | { type: 'Delete project'; data: HomeCommandSchema['Delete project'] }, + input: {} as { + navigate: ReturnType + defaultProjectName: string + defaultDirectory: string + }, + }, + actions: {} as { toastSuccess: () => void; toastError: () => void }, + actors: { + readProjects: fromPromise(async () => { + return [] + }), + createProject: fromPromise(async () => { + return + }), + renameProject: fromPromise(async () => { + return + }), + deleteProject: fromPromise(async () => { + return + }), + }, +}).createMachine( { - /** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAHTK6xampYAOATqgFZj4AusAxAMLMwuLthbtOXANoAGALqJQjVLGJdiqUopAAPRAHYAbPooAWABwBGUwE5zAJgeGArM-MAaEAE9EN0wGYKGX97GX1nGVNDS0MbfwBfeM80TBwCEnIqGiZWDm4+ACUwUlxU8TzpeW1lVXVNbT0EcJNg02d-fzt7fU77Tx8EQ0iKCPtnfUsjGRtLGXtE5IxsPCIySmpacsk+QWFRHIluWQUkEBq1DS1TxqN7ChjzOxtXf0t7a37EcwsRibH-ZzRezA8wLEApZbpNZZTa5ba8AAiYAANmB9lsjlVTuc6ldQDdDOYKP5bm0os5TDJDJ8mlEzPpzIZHA4bO9umCIWlVpkNgcKnwAPKMYp8yTHaoqC71a6IEmBUz6BkWZzWDq2Uw0qzOIJAwz+PXWfSmeZJcFLLkZSi7ERkKCi7i8CCaShkABuqAA1pR8EIRGAALQYyonJSS3ENRDA2wUeyvd6dPVhGw0-RhGOp8IA8xGFkc80rS0Ua3qUh2oO8MDMVjMCiMZEiABmqGY6AoPr2AaD4uxYcuEYQoQpQWNNjsMnMgLGKbT3TC7TcOfsNjzqQL0KKJXQtvtXEdzoobs9lCEm87cMxIbOvel+MQqtMQRmS5ks31sZpAUsZkcIX+cQZJIrpC3KUBupTbuWlbVrW9ZcE2LYUCepRnocwYSrUfYyggbzvBQ+jMq49imLYwTUt4iCft+5i-u0-7UfoQEWtCSKoiWZbnruTqZIeXoUBAKJoihFTdqGGE3rod7UdqsQTI8hiGAqrIauRA7RvYeoqhO1jtAqjFrpkLFohBHEVlWzYwY2zatvxrFCWKWKiVKeISdh4yBJE-jGs4fhhA4zg0kRNgxhplhaW0nn4XpUKZEUuAQMZqF8FxLqkO6vG+hAgYcbAIlXmJzmNERdy0RYNiKgpthxDSEU6q8MSTJYjWGFFIEULF8WljuSX7jxx7CJlQY5ZYl44pht4IP61gyPc8njt0lIuH51UKrVVITEyMy2C1hbtQl-KmdBdaWQhGVZYluWjeJjSTf402shMEyuEyljPAFL0UNmMiuN86lWHMiSmvQ-HwKcnL6WA6FOf2k3mESMRDA4RpUm4U4qf6gSEt0QIvvqfjOCaiyrtF6zZPQXWQ+GWFlUEsbmNMf1TV9NLeXDcqRIySnNaaYPEzC5M9vl-b+IyFCjupryPF9jKWP5Kks-cbMWLERHRNt0LFntkgU2NLk4dqsz43YsTK++Kk2C+MbTOOcxzOMrhqzFxTgZ1Qba1dd6BUE1jGsLMxxK9KlDNqm3tMLUQvqYlgO5QhlsTubsFXesTTUuPTfHExshDS0RftRftGgEnTZtHbX9Zr+QJ-2S4Y3qnmTC+4tMyp1EfeOnmeQqdOhyXQrFOXXCV1hCkmLDOnBJYvRRDSsyRzGjiKj0lKdAkANAA */ + /** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAMS6yzFSkDaADALqKgAOqtALsaqXYgAHogAsAJgA0IAJ6IJATgAcAOgBsAdgCsYzYoCMEsdu3LNAXwsy0mHARLlVyallKosHAE6oAVmHweWAoAYS8wXB5sbz8AnmY2JBAuXn5BJNEEAzF1MVVdXUV1JnVtCXUVGXkECW0DVTEAZgNlRvUDbINS5SsbDGw8IjIwZ1cY-0DggCUwUlw7cbiEoRTiPgEhTI6O-KZtJnMzOs0dKsRNVtU9xqbG5Wzc7V6QWwGHYdHYTx8JoNDwyLRH5LVgrbhrNKbRAPPIFMRFEplCrKM4IXL1MRdCq1C7KRSKRrPV72IZOFxfRaTCgAETAABswFFvrFAsskqt1ulQFscrCTPDiqVypU5OdlNpVBJlMUzLkVCcJET+iTHCNyczfsEAPIcWYakGJTjgzlQrK8-L8hFC5Go1oS7aFfSKJpKuyDVWqMIRPikKD6wIUCACEZkABuqAA1iN8ACogBaSnxUHs42QjLQ81wq1IkXVEzqVTSlQlJgGRoEgyut6kkZeyJkP2JihgLw+LyqDh0yIAM1QXnQqhj3rACeBrOTRtSG3TZtKheUTFLEnL2hXYlt9UaEiUBmdYjx6illesL2V7o+Mzm6Ab-p4geDqjDkZG4SvI8TbMnEOn3MQjR0hb4uoFSNGUkhMIoqKrnkByHioigQUoRRViqF6zPMN5Ni2bYdl2PC9v2qivvM75jkmhrJKmP4iH+AF4kUIFgRIEGomImKFkYyiSNo6h3EYigoeeTi0gyPqNmR95OE+UaqBA9KMqRLLkWCU5cjRs4SkwzH3Ioq56eYUHwqo2RboeoHOgYmjqIJ7zCfJYm3s2rZ9rhPZ9gOcmiYpvyfpRqmmg8BagauoHSroXHrqKaIElcRiHlxjRMCuTwnsSQkjDMuAQJhZHBEGUmkOGMkAhAo5KbAvkcmmv4IIozHGXUpYVGxByNKiFRMKoJz4icOSGGYYg2TWqiZdlvq3nlD7SS+ESlYmFUGBRVXUZkdWdQYjVdHurWos0ErLolJxcR05hWUNHqjTl5VOThnZuYRJVlZqlVUWpq31RtBhNdtrSotuEiqLpRR6IelklMeJ7uHJ8BJGltlgCp35vYgcbqKiqPnR86ruBNiMmjOnGaA0pilJomI6FKf11Pk+KtPiJTOkwg2pWe8OfLjKb+QTShqN1bHKOUOQC21UWaJZXV6V0G1MJoUopX0bps3WDmJnj1XqWDm73CYuiy0Ypiol9BZ4gN4raDomh8ZjTiXhh42q5zSOmpIUE3PkvH7KU7QmPo1sjCJjJXb8asrdCmilqofE63oEj69oUEVF1i6x7LBzStZLOK8Nl327lIfIwgZiKF1Nw3GWShJXsu31ZbiUErXuRnZn1YejqsxB3E+cBeHWvZKYMdxxu9TSvuYhNdKjR3FYVhAA */ id: 'Home machine', initial: 'Reading projects', - context: { + context: ({ input }) => ({ projects: [] as ProjectWithEntryPointMetadata[], - defaultProjectName: '', - defaultDirectory: '', - }, + ...input, + }), - on: { - assign: { - actions: assign((_, event) => ({ - ...event.data, - })), - target: '.Reading projects', - }, - }, states: { 'Has no projects': { on: { @@ -135,30 +161,9 @@ export const homeMachine = createMachine( entry: ['navigateToProject'], }, }, - - schema: { - events: {} as - | { type: 'Open project'; data: HomeCommandSchema['Open project'] } - | { type: 'Rename project'; data: HomeCommandSchema['Rename project'] } - | { type: 'Create project'; data: HomeCommandSchema['Create project'] } - | { type: 'Delete project'; data: HomeCommandSchema['Delete project'] } - | { type: 'navigate'; data: { name: string } } - | { - type: 'done.invoke.read-projects' - data: ProjectWithEntryPointMetadata[] - } - | { type: 'assign'; data: { [key: string]: any } }, }, predictableActionArguments: true, preserveActionOrder: true, - tsTypes: {} as import('./homeMachine.typegen').Typegen0, }, - { - actions: { - setProjects: assign((_, event) => { - return { projects: event.data as ProjectWithEntryPointMetadata[] } - }), - }, - } ) diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index 94769b716..bf5fce5b8 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -39,12 +39,17 @@ import { isTauri } from 'lib/isTauri' import { kclManager } from 'lang/KclSingleton' import { useLspContext } from 'components/LspProvider' import { useValidateSettings } from 'hooks/useValidateSettings' +import { AppMachineContext } from 'machines/appMachine' // This route only opens in the Tauri desktop context for now, // as defined in Router.tsx, so we can use the Tauri APIs and types. const Home = () => { useValidateSettings() - const { commandBarSend } = useCommandsContext() + const home = AppMachineContext.useSelector(s => s.children.home) + home?.on('done.invoke.read-projects', (e) => { + // do some nonsense + }) + const commands = AppMachineContext.useSelector(s => s.children.commands) const navigate = useNavigate() const { projects: loadedProjects } = useLoaderData() as HomeLoaderData const { diff --git a/yarn.lock b/yarn.lock index 33fb50662..0df2f9083 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2868,10 +2868,10 @@ "@babel/types" "^7.21.4" recast "^0.23.1" -"@xstate/react@^3.2.2": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@xstate/react/-/react-3.2.2.tgz#ddf0f9d75e2c19375b1e1b7335e72cb99762aed8" - integrity sha512-feghXWLedyq8JeL13yda3XnHPZKwYDN5HPBLykpLeuNpr9178tQd2/3d0NrH6gSd0sG5mLuLeuD+ck830fgzLQ== +"@xstate/react@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@xstate/react/-/react-4.1.0.tgz#369378951e1f7f9326700f65ed02847598aad704" + integrity sha512-Fh89luCwuMXIVXIua67d8pNuVgdGpqke2jHfIIL+ZjkfNh6YFtPDSwNSZZDhdNUsOW1zZYSbtUzbC8MIUyTSHQ== dependencies: use-isomorphic-layout-effect "^1.1.2" use-sync-external-store "^1.0.0" @@ -8372,10 +8372,10 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typescript@^5.2.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" - integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== +typescript@^5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" + integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== ua-parser-js@^1.0.35: version "1.0.35" @@ -8912,11 +8912,16 @@ ws@^8.8.0: resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.0.0-beta.54.tgz#d80f1a9e43ad883a65fc9b399161bd39633bd9bf" integrity sha512-BTnCPBQ2iTKe4uCnHEe1hNx6VTbXU+5mQGybSQHOjTLiBi4Ryi+tL9T6N1tmqagvM8rfl4XRfvndogfWCWcdpw== -xstate@^4.33.4, xstate@^4.38.2: +xstate@^4.33.4: version "4.38.3" resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.38.3.tgz#4e15e7ad3aa0ca1eea2010548a5379966d8f1075" integrity sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw== +xstate@^5.9.1: + version "5.9.1" + resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.9.1.tgz#1f167fae423cadd362762e2ff697d84fd774979c" + integrity sha512-85edx7iMqRJSRlEPevDwc98EWDYUlT5zEQ54AXuRVR+G76gFbcVTAUdtAeqOVxy8zYnUr9FBB5114iK6enljjw== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"