chore: first attempt of purging projects context provider

This commit is contained in:
Kevin Nadro
2025-04-15 10:30:52 -06:00
parent 5ac504d000
commit 0a2e668ee4
9 changed files with 110 additions and 88 deletions

View File

@ -2,7 +2,6 @@ import { AppStateProvider } from '@src/AppState'
import LspProvider from '@src/components/LspProvider' import LspProvider from '@src/components/LspProvider'
import { MachineManagerProvider } from '@src/components/MachineManagerProvider' import { MachineManagerProvider } from '@src/components/MachineManagerProvider'
import { OpenInDesktopAppHandler } from '@src/components/OpenInDesktopAppHandler' import { OpenInDesktopAppHandler } from '@src/components/OpenInDesktopAppHandler'
import { ProjectsContextProvider } from '@src/components/ProjectsContextProvider'
import { SystemIOMachineLogicListener } from '@src/components/Providers/SystemIOProviderDesktop' import { SystemIOMachineLogicListener } from '@src/components/Providers/SystemIOProviderDesktop'
import { RouteProvider } from '@src/components/RouteProvider' import { RouteProvider } from '@src/components/RouteProvider'
import { KclContextProvider } from '@src/lang/KclProvider' import { KclContextProvider } from '@src/lang/KclProvider'
@ -17,11 +16,9 @@ function RootLayout() {
} }
}, []) }, [])
return ( return (
<div>
<OpenInDesktopAppHandler> <OpenInDesktopAppHandler>
<RouteProvider> <RouteProvider>
<LspProvider> <LspProvider>
<ProjectsContextProvider>
<KclContextProvider> <KclContextProvider>
<AppStateProvider> <AppStateProvider>
<MachineManagerProvider> <MachineManagerProvider>
@ -30,11 +27,9 @@ function RootLayout() {
</MachineManagerProvider> </MachineManagerProvider>
</AppStateProvider> </AppStateProvider>
</KclContextProvider> </KclContextProvider>
</ProjectsContextProvider>
</LspProvider> </LspProvider>
</RouteProvider> </RouteProvider>
</OpenInDesktopAppHandler> </OpenInDesktopAppHandler>
</div>
) )
} }

View File

@ -1,20 +1,24 @@
import { PATHS } from '@src/lib/paths' import { PATHS } from '@src/lib/paths'
import { systemIOActor } from '@src/machines/appMachine' import { systemIOActor, useSettings } from '@src/machines/appMachine'
import { useSelector } from '@xstate/react' import {
useProjectDirectoryPath,
useRequestedFileName,
useRequestedProjectName,
useState as useSystemIOState,
} from '@src/machines/systemIO/hooks'
import { SystemIOMachineEvents } from '@src/machines/systemIO/utils'
import { useEffect } from 'react' import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
export const useRequestedProjectName = () => import { projectsCommandBarConfig } from '@src/lib/commandBarConfigs/projectsCommandConfig'
useSelector(systemIOActor, (state) => state.context.requestedProjectName) import useStateMachineCommands from '@src/hooks/useStateMachineCommands'
export const useRequestedFileName = () =>
useSelector(systemIOActor, (state) => state.context.requestedFileName)
export const useProjectDirectoryPath = () =>
useSelector(systemIOActor, (state) => state.context.projectDirectoryPath)
export function SystemIOMachineLogicListener() { export function SystemIOMachineLogicListener() {
const requestedProjectName = useRequestedProjectName() const requestedProjectName = useRequestedProjectName()
const requestedFileName = useRequestedFileName() const requestedFileName = useRequestedFileName()
const projectDirectoryPath = useProjectDirectoryPath() const projectDirectoryPath = useProjectDirectoryPath()
const navigate = useNavigate() const navigate = useNavigate()
const settings = useSettings()
const state = useSystemIOState()
// Handle global project name navigation // Handle global project name navigation
useEffect(() => { useEffect(() => {
@ -46,5 +50,27 @@ export function SystemIOMachineLogicListener() {
navigate(requestedPath) navigate(requestedPath)
}, [requestedFileName]) }, [requestedFileName])
// Reload all folders when the project directory path changes
useEffect(() => {
systemIOActor.send({
type: SystemIOMachineEvents.setProjectDirectoryPath,
data: {
requestedProjectDirectoryPath:
settings.app.projectDirectory.current || '',
},
})
}, [settings.app.projectDirectory.current])
// Implement setting the default project folder name
useEffect(() => {
systemIOActor.send({
type: SystemIOMachineEvents.setDefaultProjectFolderName,
data: {
requestedDefaultProjectFolderName:
settings.projects.defaultProjectName.current || '',
},
})
}, [settings.projects.defaultProjectName.current])
return null return null
} }

View File

@ -1,10 +1,8 @@
import { useSelector } from '@xstate/react' import { useSelector } from '@xstate/react'
import { createActor, setup, spawnChild } from 'xstate' import { createActor, setup, spawnChild } from 'xstate'
import { readAppSettingsFile } from '@src/lib/desktop'
import { isDesktop } from '@src/lib/isDesktop' import { isDesktop } from '@src/lib/isDesktop'
import { createSettings } from '@src/lib/settings/initialSettings' import { createSettings } from '@src/lib/settings/initialSettings'
import { reportRejection } from '@src/lib/trap'
import { authMachine } from '@src/machines/authMachine' import { authMachine } from '@src/machines/authMachine'
import type { EngineStreamActor } from '@src/machines/engineStreamMachine' import type { EngineStreamActor } from '@src/machines/engineStreamMachine'
import { import {
@ -15,7 +13,6 @@ import { ACTOR_IDS } from '@src/machines/machineConstants'
import { settingsMachine } from '@src/machines/settingsMachine' import { settingsMachine } from '@src/machines/settingsMachine'
import { systemIOMachineDesktop } from '@src/machines/systemIO/systemIOMachineDesktop' import { systemIOMachineDesktop } from '@src/machines/systemIO/systemIOMachineDesktop'
import { systemIOMachineWeb } from '@src/machines/systemIO/systemIOMachineWeb' import { systemIOMachineWeb } from '@src/machines/systemIO/systemIOMachineWeb'
import { SystemIOMachineEvents } from '@src/machines/systemIO/utils'
const { AUTH, SETTINGS, SYSTEM_IO, ENGINE_STREAM } = ACTOR_IDS const { AUTH, SETTINGS, SYSTEM_IO, ENGINE_STREAM } = ACTOR_IDS
const appMachineActors = { const appMachineActors = {
[AUTH]: authMachine, [AUTH]: authMachine,
@ -85,24 +82,6 @@ export const useSettings = () =>
// TODO: Debugging // TODO: Debugging
export const systemIOActor = appActor.getSnapshot().children.systemIO! export const systemIOActor = appActor.getSnapshot().children.systemIO!
async function initializeActors() {
if (isDesktop()) {
const appSettings = await readAppSettingsFile()
const projectDirectorySettting = appSettings.settings?.project?.directory
systemIOActor.send({
type: SystemIOMachineEvents.setProjectDirectoryPath,
data: {
requestedProjectDirectoryPath: projectDirectorySettting || '',
},
})
systemIOActor.send({
type: SystemIOMachineEvents.readFoldersFromProjectDirectory,
})
}
}
initializeActors().catch(reportRejection)
window.systemIOActor = systemIOActor window.systemIOActor = systemIOActor
export const engineStreamActor = appActor.system.get( export const engineStreamActor = appActor.system.get(
ENGINE_STREAM ENGINE_STREAM

View File

@ -0,0 +1,11 @@
import { systemIOActor } from '@src/machines/appMachine'
import { useSelector } from '@xstate/react'
export const useRequestedProjectName = () =>
useSelector(systemIOActor, (state) => state.context.requestedProjectName)
export const useRequestedFileName = () =>
useSelector(systemIOActor, (state) => state.context.requestedFileName)
export const useProjectDirectoryPath = () =>
useSelector(systemIOActor, (state) => state.context.projectDirectoryPath)
export const useFolders = () =>
useSelector(systemIOActor, (state) => state.context.folders)
export const useState = () => useSelector(systemIOActor, (state) => state)

View File

@ -62,5 +62,17 @@ describe('systemIOMachine - XState', () => {
expect(context.projectDirectoryPath).toBe(kclSamplesPath) expect(context.projectDirectoryPath).toBe(kclSamplesPath)
}) })
}) })
describe('when setting default project folder name', () => {
it('should set a new default project folder name', async () => {
const expected = 'coolcoolcoolProjectName'
const actor = createActor(systemIOMachineDesktop).start()
actor.send({
type: SystemIOMachineEvents.setDefaultProjectFolderName,
data: { requestedDefaultProjectFolderName: expected },
})
let context = actor.getSnapshot().context
expect(context.defaultProjectFolderName).toBe(expected)
})
})
}) })
}) })

View File

@ -58,6 +58,10 @@ export const systemIOMachine = setup({
requestedFileName: string requestedFileName: string
requestedCode: string requestedCode: string
} }
}
| {
type: SystemIOMachineEvents.setDefaultProjectFolderName
data: { requestedDefaultProjectFolderName: string }
}, },
}, },
actions: { actions: {
@ -91,6 +95,12 @@ export const systemIOMachine = setup({
} }
}, },
}), }),
[SystemIOMachineActions.setDefaultProjectFolderName]: assign({
defaultProjectFolderName: ({ event }) => {
assertEvent(event, SystemIOMachineEvents.setDefaultProjectFolderName)
return event.data.requestedDefaultProjectFolderName
},
}),
}, },
actors: { actors: {
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise( [SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(
@ -133,6 +143,7 @@ export const systemIOMachine = setup({
requestedProjectName: string requestedProjectName: string
requestedFileName: string requestedFileName: string
requestedCode: string requestedCode: string
} }
}) => {} }) => {}
), ),
@ -182,6 +193,9 @@ export const systemIOMachine = setup({
[SystemIOMachineEvents.createKCLFile]: { [SystemIOMachineEvents.createKCLFile]: {
target: SystemIOMachineStates.creatingKCLFile, target: SystemIOMachineStates.creatingKCLFile,
}, },
[SystemIOMachineEvents.setDefaultProjectFolderName]: {
actions: [SystemIOMachineActions.setDefaultProjectFolderName],
},
}, },
}, },
[SystemIOMachineStates.readingFolders]: { [SystemIOMachineStates.readingFolders]: {

View File

@ -32,6 +32,7 @@ export enum SystemIOMachineEvents {
renameProject = 'rename project', renameProject = 'rename project',
deleteProject = 'delete project', deleteProject = 'delete project',
createKCLFile = 'create kcl file', createKCLFile = 'create kcl file',
setDefaultProjectFolderName = 'set default project folder name',
} }
export enum SystemIOMachineActions { export enum SystemIOMachineActions {
@ -39,6 +40,7 @@ export enum SystemIOMachineActions {
setProjectDirectoryPath = 'set project directory path', setProjectDirectoryPath = 'set project directory path',
setRequestedProjectName = 'set requested project name', setRequestedProjectName = 'set requested project name',
setRequestedFileName = 'set requested file name', setRequestedFileName = 'set requested file name',
setDefaultProjectFolderName = 'set default project folder name',
} }
export const NO_PROJECT_DIRECTORY = '' export const NO_PROJECT_DIRECTORY = ''

View File

@ -15,7 +15,6 @@ import {
} from '@src/components/ProjectSearchBar' } from '@src/components/ProjectSearchBar'
import { useCreateFileLinkQuery } from '@src/hooks/useCreateFileLinkQueryWatcher' import { useCreateFileLinkQuery } from '@src/hooks/useCreateFileLinkQueryWatcher'
import { useMenuListener } from '@src/hooks/useMenu' import { useMenuListener } from '@src/hooks/useMenu'
import { useProjectsContext } from '@src/hooks/useProjectsContext'
import { isDesktop } from '@src/lib/isDesktop' import { isDesktop } from '@src/lib/isDesktop'
import { PATHS } from '@src/lib/paths' import { PATHS } from '@src/lib/paths'
import { markOnce } from '@src/lib/performance' import { markOnce } from '@src/lib/performance'
@ -27,14 +26,22 @@ import {
getSortIcon, getSortIcon,
} from '@src/lib/sorting' } from '@src/lib/sorting'
import { reportRejection } from '@src/lib/trap' import { reportRejection } from '@src/lib/trap'
import { authActor, useSettings } from '@src/machines/appMachine' import { authActor, systemIOActor, useSettings } from '@src/machines/appMachine'
import { commandBarActor } from '@src/machines/commandBarMachine' import { commandBarActor } from '@src/machines/commandBarMachine'
import {
useFolders,
useState as useSystemIOState,
} from '@src/machines/systemIO/hooks'
import {
SystemIOMachineEvents,
SystemIOMachineStates,
} from '@src/machines/systemIO/utils'
import type { WebContentSendPayload } from '@src/menu/channels' import type { WebContentSendPayload } from '@src/menu/channels'
// This route only opens in the desktop context for now, // This route only opens in the desktop context for now,
// as defined in Router.tsx, so we can use the desktop APIs and types. // as defined in Router.tsx, so we can use the desktop APIs and types.
const Home = () => { const Home = () => {
const { state, send } = useProjectsContext() const state = useSystemIOState()
const [readWriteProjectDir, setReadWriteProjectDir] = useState<{ const [readWriteProjectDir, setReadWriteProjectDir] = useState<{
value: boolean value: boolean
error: unknown error: unknown
@ -156,40 +163,13 @@ const Home = () => {
} }
) )
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const projects = useFolders()
const projects = state?.context.projects ?? []
const [searchParams, setSearchParams] = useSearchParams() const [searchParams, setSearchParams] = useSearchParams()
const { searchResults, query, setQuery } = useProjectSearch(projects) const { searchResults, query, setQuery } = useProjectSearch(projects)
const sort = searchParams.get('sort_by') ?? 'modified:desc' const sort = searchParams.get('sort_by') ?? 'modified:desc'
const isSortByModified = sort?.includes('modified') || !sort || sort === null const isSortByModified = sort?.includes('modified') || !sort || sort === null
// Update the default project name and directory in the home machine
// when the settings change
useEffect(() => {
send({
type: 'assign',
data: {
defaultProjectName: settings.projects.defaultProjectName.current,
defaultDirectory: settings.app.projectDirectory.current,
},
})
// Must be a truthy string, not '' or null or undefined
if (settings.app.projectDirectory.current) {
window.electron
.canReadWriteDirectory(settings.app.projectDirectory.current)
.then((res) => {
setReadWriteProjectDir(res)
})
.catch(reportRejection)
}
}, [
settings.app.projectDirectory.current,
settings.projects.defaultProjectName.current,
send,
])
async function handleRenameProject( async function handleRenameProject(
e: FormEvent<HTMLFormElement>, e: FormEvent<HTMLFormElement>,
project: Project project: Project
@ -204,17 +184,20 @@ const Home = () => {
} }
if (newProjectName !== project.name) { if (newProjectName !== project.name) {
send({ systemIOActor.send({
type: 'Rename project', type: SystemIOMachineEvents.renameProject,
data: { oldName: project.name, newName: newProjectName as string }, data: {
requestedProjectName: String(newProjectName),
projectName: project.name,
},
}) })
} }
} }
async function handleDeleteProject(project: Project) { async function handleDeleteProject(project: Project) {
send({ systemIOActor.send({
type: 'Delete project', type: SystemIOMachineEvents.deleteProject,
data: { name: project.name || '' }, data: { requestedProjectName: project.name },
}) })
} }
/** Type narrowing function of unknown error to a string */ /** Type narrowing function of unknown error to a string */
@ -345,7 +328,7 @@ const Home = () => {
data-testid="home-section" data-testid="home-section"
className="flex-1 overflow-y-auto pr-2 pb-24" className="flex-1 overflow-y-auto pr-2 pb-24"
> >
{state?.matches('Reading projects') ? ( {state?.matches(SystemIOMachineStates.readingFolders) ? (
<Loading>Loading your Projects...</Loading> <Loading>Loading your Projects...</Loading>
) : ( ) : (
<> <>