Make empty defaultProjectName value impossible (#409)
				
					
				
			* Set named const as default project name * Refactor: move base units into settings machine Signed off by Frank Noirot <frank@kittycad.io> * Reset default when creating with blank name Signed off by Frank Noirot <frank@kittycad.io> * Make it impossible to set empty defaultProjectName Signed off by Frank Noirot <frank@kittycad.io> * Make it impossible to assign empty strings to defaultProjectName Signed off by Frank Noirot <frank@kittycad.io>
This commit is contained in:
		@ -1,13 +1,25 @@
 | 
			
		||||
import { assign, createMachine } from 'xstate'
 | 
			
		||||
import { BaseUnit, baseUnitsUnion } from '../useStore'
 | 
			
		||||
import { CommandBarMeta } from '../lib/commands'
 | 
			
		||||
import { Themes, getSystemTheme, setThemeClass } from '../lib/theme'
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_PROJECT_NAME = 'project-$nnn'
 | 
			
		||||
 | 
			
		||||
export enum UnitSystem {
 | 
			
		||||
  Imperial = 'imperial',
 | 
			
		||||
  Metric = 'metric',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const baseUnits = {
 | 
			
		||||
  imperial: ['in', 'ft'],
 | 
			
		||||
  metric: ['mm', 'cm', 'm'],
 | 
			
		||||
} as const
 | 
			
		||||
 | 
			
		||||
export type BaseUnit = 'in' | 'ft' | 'mm' | 'cm' | 'm'
 | 
			
		||||
 | 
			
		||||
export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v)
 | 
			
		||||
 | 
			
		||||
export type Toggle = 'On' | 'Off'
 | 
			
		||||
 | 
			
		||||
export const SETTINGS_PERSIST_KEY = 'SETTINGS_PERSIST_KEY'
 | 
			
		||||
 | 
			
		||||
export const settingsCommandBarMeta: CommandBarMeta = {
 | 
			
		||||
@ -85,11 +97,11 @@ export const settingsMachine = createMachine(
 | 
			
		||||
    predictableActionArguments: true,
 | 
			
		||||
    context: {
 | 
			
		||||
      theme: Themes.System,
 | 
			
		||||
      defaultProjectName: '',
 | 
			
		||||
      defaultProjectName: DEFAULT_PROJECT_NAME,
 | 
			
		||||
      unitSystem: UnitSystem.Imperial,
 | 
			
		||||
      baseUnit: 'in' as BaseUnit,
 | 
			
		||||
      defaultDirectory: '',
 | 
			
		||||
      textWrapping: 'On' as 'On' | 'Off',
 | 
			
		||||
      textWrapping: 'On' as Toggle,
 | 
			
		||||
      showDebugPanel: false,
 | 
			
		||||
      onboardingStatus: '',
 | 
			
		||||
    },
 | 
			
		||||
@ -113,7 +125,8 @@ export const settingsMachine = createMachine(
 | 
			
		||||
          'Set Default Project Name': {
 | 
			
		||||
            actions: [
 | 
			
		||||
              assign({
 | 
			
		||||
                defaultProjectName: (_, event) => event.data.defaultProjectName,
 | 
			
		||||
                defaultProjectName: (_, event) =>
 | 
			
		||||
                  event.data.defaultProjectName.trim() || DEFAULT_PROJECT_NAME,
 | 
			
		||||
              }),
 | 
			
		||||
              'persistSettings',
 | 
			
		||||
              'toastSuccess',
 | 
			
		||||
@ -205,7 +218,7 @@ export const settingsMachine = createMachine(
 | 
			
		||||
            data: { unitSystem: UnitSystem }
 | 
			
		||||
          }
 | 
			
		||||
        | { type: 'Set Base Unit'; data: { baseUnit: BaseUnit } }
 | 
			
		||||
        | { type: 'Set Text Wrapping'; data: { textWrapping: 'On' | 'Off' } }
 | 
			
		||||
        | { type: 'Set Text Wrapping'; data: { textWrapping: Toggle } }
 | 
			
		||||
        | { type: 'Set Onboarding Status'; data: { onboardingStatus: string } }
 | 
			
		||||
        | { type: 'Toggle Debug Panel' },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ import {
 | 
			
		||||
import useStateMachineCommands from '../hooks/useStateMachineCommands'
 | 
			
		||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
 | 
			
		||||
import { useCommandsContext } from 'hooks/useCommandsContext'
 | 
			
		||||
import { DEFAULT_PROJECT_NAME } from 'machines/settingsMachine'
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
@ -38,6 +39,7 @@ const Home = () => {
 | 
			
		||||
  const {
 | 
			
		||||
    settings: {
 | 
			
		||||
      context: { defaultDirectory, defaultProjectName },
 | 
			
		||||
      send: sendToSettings,
 | 
			
		||||
    },
 | 
			
		||||
  } = useGlobalStateContext()
 | 
			
		||||
 | 
			
		||||
@ -71,16 +73,33 @@ const Home = () => {
 | 
			
		||||
        context: ContextFrom<typeof homeMachine>,
 | 
			
		||||
        event: EventFrom<typeof homeMachine, 'Create project'>
 | 
			
		||||
      ) => {
 | 
			
		||||
        let name =
 | 
			
		||||
        let name = (
 | 
			
		||||
          event.data && 'name' in event.data
 | 
			
		||||
            ? event.data.name
 | 
			
		||||
            : defaultProjectName
 | 
			
		||||
        ).trim()
 | 
			
		||||
        let shouldUpdateDefaultProjectName = false
 | 
			
		||||
 | 
			
		||||
        // If there is no default project name, flag it to be set to the default
 | 
			
		||||
        if (!name) {
 | 
			
		||||
          name = DEFAULT_PROJECT_NAME
 | 
			
		||||
          shouldUpdateDefaultProjectName = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (doesProjectNameNeedInterpolated(name)) {
 | 
			
		||||
          const nextIndex = await getNextProjectIndex(name, projects)
 | 
			
		||||
          name = interpolateProjectNameWithIndex(name, nextIndex)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await createNewProject(context.defaultDirectory + '/' + name)
 | 
			
		||||
 | 
			
		||||
        if (shouldUpdateDefaultProjectName) {
 | 
			
		||||
          sendToSettings({
 | 
			
		||||
            type: 'Set Default Project Name',
 | 
			
		||||
            data: { defaultProjectName: DEFAULT_PROJECT_NAME },
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return `Successfully created "${name}"`
 | 
			
		||||
      },
 | 
			
		||||
      renameProject: async (
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
 | 
			
		||||
import { BaseUnit, baseUnits } from '../../useStore'
 | 
			
		||||
import { BaseUnit, baseUnits } from '../../machines/settingsMachine'
 | 
			
		||||
import { ActionButton } from '../../components/ActionButton'
 | 
			
		||||
import { SettingsSection } from '../Settings'
 | 
			
		||||
import { Toggle } from '../../components/Toggle/Toggle'
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,11 @@ import {
 | 
			
		||||
import { ActionButton } from '../components/ActionButton'
 | 
			
		||||
import { AppHeader } from '../components/AppHeader'
 | 
			
		||||
import { open } from '@tauri-apps/api/dialog'
 | 
			
		||||
import { BaseUnit, baseUnits } from '../useStore'
 | 
			
		||||
import {
 | 
			
		||||
  BaseUnit,
 | 
			
		||||
  DEFAULT_PROJECT_NAME,
 | 
			
		||||
  baseUnits,
 | 
			
		||||
} from '../machines/settingsMachine'
 | 
			
		||||
import { Toggle } from '../components/Toggle/Toggle'
 | 
			
		||||
import { useLocation, useNavigate, useRouteLoaderData } from 'react-router-dom'
 | 
			
		||||
import { useHotkeys } from 'react-hotkeys-hook'
 | 
			
		||||
@ -118,10 +122,14 @@ export const Settings = () => {
 | 
			
		||||
                className="block w-full px-3 py-1 border border-chalkboard-30 bg-transparent"
 | 
			
		||||
                defaultValue={defaultProjectName}
 | 
			
		||||
                onBlur={(e) => {
 | 
			
		||||
                  const newValue = e.target.value.trim() || DEFAULT_PROJECT_NAME
 | 
			
		||||
                  send({
 | 
			
		||||
                    type: 'Set Default Project Name',
 | 
			
		||||
                    data: { defaultProjectName: e.target.value },
 | 
			
		||||
                    data: {
 | 
			
		||||
                      defaultProjectName: newValue,
 | 
			
		||||
                    },
 | 
			
		||||
                  })
 | 
			
		||||
                  e.target.value = newValue
 | 
			
		||||
                }}
 | 
			
		||||
                autoCapitalize="off"
 | 
			
		||||
                autoComplete="off"
 | 
			
		||||
 | 
			
		||||
@ -94,15 +94,6 @@ export type GuiModes =
 | 
			
		||||
      position: Position
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
export const baseUnits = {
 | 
			
		||||
  imperial: ['in', 'ft'],
 | 
			
		||||
  metric: ['mm', 'cm', 'm'],
 | 
			
		||||
} as const
 | 
			
		||||
 | 
			
		||||
export type BaseUnit = 'in' | 'ft' | 'mm' | 'cm' | 'm'
 | 
			
		||||
 | 
			
		||||
export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v)
 | 
			
		||||
 | 
			
		||||
export type PaneType =
 | 
			
		||||
  | 'code'
 | 
			
		||||
  | 'variables'
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user