Rearchitect settings system to be scoped (#1956)
* BROKEN: start of scopes for each setting * Clean up later: mostly-functional scoped settings! Broken command bar, unimplemented generated settings components * Working persisted project settings in-folder * Start working toward automatic commands and settings UI * Relatively stable, settings-menu-editable * Settings persistence tweaks after merge * Custom settings UI working properly, cleaner types * Allow boolean command types, create Settings UI for them * Add support for option and string Settings input types * Proof of concept settings from command bar * Add all settings to command bar * Allow settings to be hidden on a level * Better command titles for settings * Hide the settings the settings from the commands bar * Derive command defaultValue from *current* settingsMachine context * Fix generated settings UI for 'options' type settings * Pretty settings modal 💅 * Allow for rollback to parent level setting * fmt * Fix tsc errors not related to loading from localStorage * Better setting descriptions, better buttons * Make displayName searchable in command bar * Consolidate constants, get working in browser * Start fixing tests, better types for saved settings payloads * Fix playwright tests * Add a test for the settings modal * Add AtLeast to codespell ignore list * Goofed merge of codespellrc * Try fixing linux E2E tests * Make codespellrc word lowercase * fmt * Fix data-testid in Tauri test * Don't set text settings if nothing changed * Turn off unimplemented settings * Allow for multiple "execution-done" messages to have appeared in snapshot tests * Try fixing up snapshot tests * Switch from .json to .toml settings file format * Use a different method for overriding the default units * Try to force using the new common storage state in snapshot tests * Update tests to use TOML * fmt and remove console logs * Restore units to export * tsc errors, make snapshot tests use TOML * Ensure that snapshot tests use the basicStorageState * Re-organize use of test.use() * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Update snapshots one more time since lighting changed * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Fix broken "Show in folder" for project-level settings * Fire all relevant actions after settings reset * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Properly reset the default directory * Hide settings by platform * Actually honor showDebugPanel * Unify settings hiding logic * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * fix first extrusion snapshot * another attempt to fix extrustion snapshot * Rerun test suite * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * trigger CI * more extrusion stuff * Replace resetSettings console log with comment --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
This commit is contained in:
@ -1,15 +1,18 @@
|
||||
import { ActionFunction, LoaderFunction, redirect } from 'react-router-dom'
|
||||
import { HomeLoaderData, IndexLoaderData } from './types'
|
||||
import { FileLoaderData, 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 { getProjectMetaByRouteId, paths } from './paths'
|
||||
import { BROWSER_PATH } from 'lib/paths'
|
||||
import {
|
||||
BROWSER_FILE_NAME,
|
||||
BROWSER_PROJECT_NAME,
|
||||
PROJECT_ENTRYPOINT,
|
||||
} 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'
|
||||
@ -20,17 +23,32 @@ 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()
|
||||
export const settingsLoader: LoaderFunction = async ({
|
||||
params,
|
||||
}): ReturnType<typeof loadAndValidateSettings> => {
|
||||
let settings = await loadAndValidateSettings()
|
||||
|
||||
// I don't love that we have to read the settings again here,
|
||||
// but we need to get the project path to load the project settings
|
||||
if (params.id) {
|
||||
const defaultDir = settings.app.projectDirectory.current || ''
|
||||
const projectPathData = getProjectMetaByRouteId(params.id, defaultDir)
|
||||
if (projectPathData) {
|
||||
const { projectPath } = projectPathData
|
||||
settings = await loadAndValidateSettings(projectPath)
|
||||
}
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
// 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)
|
||||
export const onboardingRedirectLoader: ActionFunction = async (args) => {
|
||||
const settings = await loadAndValidateSettings()
|
||||
const onboardingStatus = settings.app.onboardingStatus.current || ''
|
||||
const notEnRouteToOnboarding = !args.request.url.includes(
|
||||
paths.ONBOARDING.INDEX
|
||||
)
|
||||
// '' is the initial state, 'done' and 'dismissed' are the final states
|
||||
const hasValidOnboardingStatus =
|
||||
onboardingStatus.length === 0 ||
|
||||
@ -44,34 +62,33 @@ export const onboardingRedirectLoader: ActionFunction = async ({ request }) => {
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
return settingsLoader(args)
|
||||
}
|
||||
|
||||
export const fileLoader: LoaderFunction = async ({
|
||||
params,
|
||||
}): Promise<IndexLoaderData | Response> => {
|
||||
const { settings } = await loadAndValidateSettings()
|
||||
}): Promise<FileLoaderData | Response> => {
|
||||
let settings = await loadAndValidateSettings()
|
||||
|
||||
const defaultDir = settings.defaultDirectory || ''
|
||||
const defaultDir = settings.app.projectDirectory.current || '/'
|
||||
const projectPathData = getProjectMetaByRouteId(params.id, defaultDir)
|
||||
const isBrowserProject = params.id === decodeURIComponent(BROWSER_PATH)
|
||||
|
||||
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 (!isBrowserProject && projectPathData) {
|
||||
const { projectName, projectPath, currentFileName, currentFilePath } =
|
||||
projectPathData
|
||||
|
||||
if (firstSlashIndex === -1 || !currentFileName)
|
||||
if (!currentFileName || !currentFilePath) {
|
||||
return redirect(
|
||||
`${paths.FILE}/${encodeURIComponent(
|
||||
`${params.id}${sep}${PROJECT_ENTRYPOINT}`
|
||||
`${params.id}${isTauri() ? sep : '/'}${PROJECT_ENTRYPOINT}`
|
||||
)}`
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: PROJECT_ENTRYPOINT is hardcoded
|
||||
// until we support setting a project's entrypoint file
|
||||
const code = await readTextFile(decodedId)
|
||||
const code = await readTextFile(currentFilePath)
|
||||
const entrypointMetadata = await metadata(
|
||||
projectPath + sep + PROJECT_ENTRYPOINT
|
||||
)
|
||||
@ -82,7 +99,7 @@ export const fileLoader: LoaderFunction = async ({
|
||||
// So that WASM gets an updated path for operations
|
||||
fileSystemManager.dir = projectPath
|
||||
|
||||
return {
|
||||
const projectData: IndexLoaderData = {
|
||||
code,
|
||||
project: {
|
||||
name: projectName,
|
||||
@ -92,13 +109,26 @@ export const fileLoader: LoaderFunction = async ({
|
||||
},
|
||||
file: {
|
||||
name: currentFileName,
|
||||
path: params.id,
|
||||
path: currentFilePath,
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
...projectData,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: '',
|
||||
project: {
|
||||
name: BROWSER_PROJECT_NAME,
|
||||
path: '/' + BROWSER_PROJECT_NAME,
|
||||
children: [],
|
||||
},
|
||||
file: {
|
||||
name: BROWSER_FILE_NAME,
|
||||
path: decodeURIComponent(BROWSER_PATH),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,23 +138,15 @@ export const homeLoader: LoaderFunction = async (): Promise<
|
||||
HomeLoaderData | Response
|
||||
> => {
|
||||
if (!isTauri()) {
|
||||
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
||||
return redirect(paths.FILE + '/' + BROWSER_PROJECT_NAME)
|
||||
}
|
||||
const { settings } = await loadAndValidateSettings()
|
||||
const settings = await loadAndValidateSettings()
|
||||
|
||||
const projectDir = await initializeProjectDirectory(
|
||||
settings.defaultDirectory || (await getInitialDefaultDir())
|
||||
settings.app.projectDirectory.current || (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 {
|
||||
|
Reference in New Issue
Block a user