2024-03-14 15:56:45 -04:00
|
|
|
import { ActionFunction, LoaderFunction, redirect } from 'react-router-dom'
|
|
|
|
import { HomeLoaderData, IndexLoaderData } from './types'
|
|
|
|
import { isTauri } from './isTauri'
|
|
|
|
import { paths } from './paths'
|
|
|
|
import { BROWSER_FILE_NAME } from 'Router'
|
|
|
|
import { SETTINGS_PERSIST_KEY } from 'lib/constants'
|
|
|
|
import { loadAndValidateSettings } from './settings/settingsUtils'
|
|
|
|
import {
|
|
|
|
getInitialDefaultDir,
|
|
|
|
getProjectsInDir,
|
|
|
|
initializeProjectDirectory,
|
|
|
|
PROJECT_ENTRYPOINT,
|
|
|
|
} from './tauriFS'
|
|
|
|
import makeUrlPathRelative from './makeUrlPathRelative'
|
|
|
|
import { sep } from '@tauri-apps/api/path'
|
|
|
|
import { readDir, readTextFile } from '@tauri-apps/api/fs'
|
|
|
|
import { metadata } from 'tauri-plugin-fs-extra-api'
|
2024-03-22 16:55:30 +11:00
|
|
|
import { kclManager } from 'lib/singletons'
|
2024-03-14 15:56:45 -04:00
|
|
|
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
|
|
|
|
|
|
|
// The root loader simply resolves the settings and any errors that
|
|
|
|
// occurred during the settings load
|
|
|
|
export const indexLoader: LoaderFunction = async (): ReturnType<
|
|
|
|
typeof loadAndValidateSettings
|
|
|
|
> => {
|
|
|
|
return await loadAndValidateSettings()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Redirect users to the appropriate onboarding page if they haven't completed it
|
|
|
|
export const onboardingRedirectLoader: ActionFunction = async ({ request }) => {
|
|
|
|
const { settings } = await loadAndValidateSettings()
|
|
|
|
const onboardingStatus = settings.onboardingStatus || ''
|
|
|
|
const notEnRouteToOnboarding = !request.url.includes(paths.ONBOARDING.INDEX)
|
|
|
|
// '' is the initial state, 'done' and 'dismissed' are the final states
|
|
|
|
const hasValidOnboardingStatus =
|
|
|
|
onboardingStatus.length === 0 ||
|
|
|
|
!(onboardingStatus === 'done' || onboardingStatus === 'dismissed')
|
|
|
|
const shouldRedirectToOnboarding =
|
|
|
|
notEnRouteToOnboarding && hasValidOnboardingStatus
|
|
|
|
|
|
|
|
if (shouldRedirectToOnboarding) {
|
|
|
|
return redirect(
|
|
|
|
makeUrlPathRelative(paths.ONBOARDING.INDEX) + onboardingStatus.slice(1)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
export const fileLoader: LoaderFunction = async ({
|
|
|
|
params,
|
|
|
|
}): Promise<IndexLoaderData | Response> => {
|
|
|
|
const { settings } = await loadAndValidateSettings()
|
|
|
|
|
|
|
|
const defaultDir = settings.defaultDirectory || ''
|
|
|
|
|
|
|
|
if (params.id && params.id !== BROWSER_FILE_NAME) {
|
|
|
|
const decodedId = decodeURIComponent(params.id)
|
|
|
|
const projectAndFile = decodedId.replace(defaultDir + sep, '')
|
|
|
|
const firstSlashIndex = projectAndFile.indexOf(sep)
|
|
|
|
const projectName = projectAndFile.slice(0, firstSlashIndex)
|
|
|
|
const projectPath = defaultDir + sep + projectName
|
|
|
|
const currentFileName = projectAndFile.slice(firstSlashIndex + 1)
|
|
|
|
|
|
|
|
if (firstSlashIndex === -1 || !currentFileName)
|
|
|
|
return redirect(
|
|
|
|
`${paths.FILE}/${encodeURIComponent(
|
|
|
|
`${params.id}${sep}${PROJECT_ENTRYPOINT}`
|
|
|
|
)}`
|
|
|
|
)
|
|
|
|
|
|
|
|
// TODO: PROJECT_ENTRYPOINT is hardcoded
|
|
|
|
// until we support setting a project's entrypoint file
|
|
|
|
const code = await readTextFile(decodedId)
|
|
|
|
const entrypointMetadata = await metadata(
|
|
|
|
projectPath + sep + PROJECT_ENTRYPOINT
|
|
|
|
)
|
|
|
|
const children = await readDir(projectPath, { recursive: true })
|
|
|
|
kclManager.setCodeAndExecute(code, false)
|
|
|
|
|
|
|
|
// Set the file system manager to the project path
|
|
|
|
// So that WASM gets an updated path for operations
|
|
|
|
fileSystemManager.dir = projectPath
|
|
|
|
|
|
|
|
return {
|
|
|
|
code,
|
|
|
|
project: {
|
|
|
|
name: projectName,
|
|
|
|
path: projectPath,
|
|
|
|
children,
|
|
|
|
entrypointMetadata,
|
|
|
|
},
|
|
|
|
file: {
|
|
|
|
name: currentFileName,
|
|
|
|
path: params.id,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
code: '',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loads the settings and by extension the projects in the default directory
|
|
|
|
// and returns them to the Home route, along with any errors that occurred
|
|
|
|
export const homeLoader: LoaderFunction = async (): Promise<
|
|
|
|
HomeLoaderData | Response
|
|
|
|
> => {
|
|
|
|
if (!isTauri()) {
|
|
|
|
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
|
|
|
}
|
|
|
|
const { settings } = await loadAndValidateSettings()
|
|
|
|
const projectDir = await initializeProjectDirectory(
|
|
|
|
settings.defaultDirectory || (await getInitialDefaultDir())
|
|
|
|
)
|
|
|
|
|
|
|
|
if (projectDir.path) {
|
|
|
|
if (projectDir.path !== settings.defaultDirectory) {
|
|
|
|
localStorage.setItem(
|
|
|
|
SETTINGS_PERSIST_KEY,
|
|
|
|
JSON.stringify({
|
|
|
|
...settings,
|
|
|
|
defaultDirectory: projectDir,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
const projects = await getProjectsInDir(projectDir.path)
|
|
|
|
|
|
|
|
return {
|
|
|
|
projects,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
projects: [],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|