Files
modeling-app/src/lib/commandBarConfigs/settingsCommandConfig.ts
Farzad Yousefzadeh 5f8d4f8294 Migrate to XState v5 (#3735)
* migrate settingsMachine

* Guard events with properties instead

* migrate settingsMachine

* Migrate auth machine

* Migrate file machine

* Migrate depracated types

* Migrate home machine

* Migrate command bar machine

* Version fixes

* Migrate command bar machine

* Migrate modeling machine

* Migrate types, state.can, state.matches and state.nextEvents

* Fix syntax

* Pass in modelingState into editor manager instead of modeling event

* Fix issue with missing command bar provider

* Fix state transition

* Fix type issue in Home

* Make sure no guards rely on event type

* Fix up command bar submission logic

* Home machine tweaks to get things running

* Fix AST fillet function args

* Handle "Set selection" when it is called by actor onDone

* Remove unused imports

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* Fix injectin project to the fileTree machine

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)"

This reverts commit 4b43ff69d1.

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* Re-run CI

* Restore success toasts on file/folder deletion

* Replace casting with guarding against event.type

* Remove console.log

Co-authored-by: Jonathan Tran <jonnytran@gmail.com>

* Replace all instances of event casting with guards against event.type

---------

Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
2024-09-09 12:59:36 -04:00

150 lines
4.3 KiB
TypeScript

import {
Command,
CommandArgument,
CommandArgumentConfig,
} from '../commandTypes'
import {
SettingsPaths,
SettingsLevel,
SettingProps,
} from 'lib/settings/settingsTypes'
import { settingsMachine } from 'machines/settingsMachine'
import { PathValue } from 'lib/types'
import { Actor, AnyStateMachine, ContextFrom } from 'xstate'
import { getPropertyByPath } from 'lib/objectPropertyByPath'
import { buildCommandArgument } from 'lib/createMachineCommand'
import decamelize from 'decamelize'
import { isDesktop } from 'lib/isDesktop'
import { Setting } from 'lib/settings/initialSettings'
// An array of the paths to all of the settings that have commandConfigs
export const settingsWithCommandConfigs = (
s: ContextFrom<typeof settingsMachine>
) =>
Object.entries(s).flatMap(([categoryName, categorySettings]) =>
Object.entries(categorySettings)
.filter(([_, setting]) => setting.commandConfig !== undefined)
.map(([settingName]) => `${categoryName}.${settingName}`)
) as SettingsPaths[]
const levelArgConfig = <T extends AnyStateMachine = AnyStateMachine>(
actor: Actor<T>,
isProjectAvailable: boolean,
hideOnLevel?: SettingsLevel
): CommandArgument<SettingsLevel, T> => ({
inputType: 'options' as const,
required: true,
defaultValue:
isProjectAvailable && hideOnLevel !== 'project' ? 'project' : 'user',
skip: true,
options:
isProjectAvailable && hideOnLevel !== 'project'
? [
{ name: 'User', value: 'user' as SettingsLevel },
{
name: 'Project',
value: 'project' as SettingsLevel,
isCurrent: true,
},
]
: [{ name: 'User', value: 'user' as SettingsLevel, isCurrent: true }],
machineActor: actor,
})
interface CreateSettingsArgs {
type: SettingsPaths
send: Function
context: ContextFrom<typeof settingsMachine>
actor: Actor<typeof settingsMachine>
isProjectAvailable: boolean
}
// Takes a Setting with a commandConfig and creates a Command
// that can be used in the CommandBar component.
export function createSettingsCommand({
type,
send,
context,
actor,
isProjectAvailable,
}: CreateSettingsArgs) {
type S = PathValue<typeof context, typeof type>
const settingConfig = getPropertyByPath(context, type) as SettingProps<
S['default']
>
const valueArgPartialConfig = settingConfig['commandConfig']
const shouldHideOnThisLevel =
settingConfig?.hideOnLevel === 'user' && !isProjectAvailable
const shouldHideOnThisPlatform =
settingConfig.hideOnPlatform &&
(isDesktop()
? settingConfig.hideOnPlatform === 'desktop'
: settingConfig.hideOnPlatform === 'web')
if (
!valueArgPartialConfig ||
shouldHideOnThisLevel ||
shouldHideOnThisPlatform
)
return null
let valueArgConfig = {
...valueArgPartialConfig,
required: true,
} as CommandArgumentConfig<S['default']>
// If the setting is a boolean, we coerce it into an options input type
if (valueArgConfig.inputType === 'boolean') {
valueArgConfig = {
...valueArgConfig,
inputType: 'options',
options: (cmdBarContext, machineContext) => {
const setting = getPropertyByPath(
machineContext,
type
) as Setting<boolean>
const level = cmdBarContext.argumentsToSubmit.level as SettingsLevel
const isCurrent =
setting[level] === undefined
? setting.getFallback(level) === true
: setting[level] === true
return [
{ name: 'On', value: true, isCurrent },
{ name: 'Off', value: false, isCurrent: !isCurrent },
]
},
}
}
// @ts-ignore - TODO figure out this typing for valueArgConfig
const valueArg = buildCommandArgument(valueArgConfig, context, actor)
const command: Command = {
name: type,
displayName: `Settings · ${decamelize(type.replaceAll('.', ' · '), {
separator: ' ',
})}`,
description: settingConfig.description,
groupId: 'settings',
icon: 'settings',
needsReview: false,
onSubmit: (data) => {
if (data !== undefined && data !== null) {
send({ type: `set.${type}`, data })
} else {
send({ type })
}
},
args: {
level: levelArgConfig(
actor,
isProjectAvailable,
settingConfig.hideOnLevel
),
value: valueArg,
},
}
return command
}