Skip to content

Commit 13271db

Browse files
committed
fully implement logic for custom commands TODO add invoking
1 parent 18b6971 commit 13271db

5 files changed

Lines changed: 182 additions & 50 deletions

File tree

src/customCommands.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { guiOptionsScheme, tryFindOptionConfig } from './optionsGuiScheme'
2+
import { options } from './optionsStorage'
3+
4+
export const customCommandsConfig = {
5+
chat: {
6+
input: [
7+
{
8+
type: 'text',
9+
placeholder: 'Command to send e.g. gamemode creative'
10+
}
11+
],
12+
handler ([command]) {
13+
bot.chat(`/${command.replace(/^\//, '')}`)
14+
}
15+
},
16+
setOrToggleSetting: {
17+
input: [
18+
{
19+
type: 'select',
20+
// maybe title case?
21+
options: Object.keys(options)
22+
},
23+
{
24+
type: 'select',
25+
options: ['toggle', 'set']
26+
},
27+
([setting = '', action = ''] = []) => {
28+
const value = options[setting]
29+
if (!action || value === undefined || action === 'toggle') return null
30+
if (action === 'set') {
31+
const getBase = () => {
32+
const config = tryFindOptionConfig(setting as any)
33+
if (config && 'values' in config) {
34+
return {
35+
type: 'select',
36+
options: config.values
37+
}
38+
}
39+
if (config?.type === 'toggle' || typeof value === 'boolean') {
40+
return {
41+
type: 'select',
42+
options: ['true', 'false']
43+
}
44+
}
45+
if (config?.type === 'slider' || value.type === 'number') {
46+
return {
47+
type: 'number',
48+
}
49+
}
50+
return {
51+
type: 'text'
52+
}
53+
}
54+
return {
55+
...getBase(),
56+
placeholder: value
57+
}
58+
}
59+
}
60+
],
61+
handler ([setting, action, value]) {
62+
if (action === 'toggle') {
63+
const value = options[setting]
64+
const config = tryFindOptionConfig(setting)
65+
if (config && 'values' in config && config.values) {
66+
const { values } = config
67+
const currentIndex = values.indexOf(value)
68+
const nextIndex = (currentIndex + 1) % values.length
69+
options[setting] = values[nextIndex]
70+
} else {
71+
options[setting] = typeof value === 'boolean' ? !value : typeof value === 'number' ? value + 1 : value
72+
}
73+
} else {
74+
options[setting] = value
75+
}
76+
}
77+
},
78+
jsScripts: {
79+
input: [
80+
{
81+
type: 'text',
82+
placeholder: 'JavaScript code to run in main thread (sensitive!)'
83+
}
84+
],
85+
handler ([code]) {
86+
// eslint-disable-next-line no-eval -- this is a feature, not a bug
87+
eval(code)
88+
}
89+
},
90+
// openCommandsScreen: {}
91+
}

src/optionsGuiScheme.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,15 @@ const Category = ({ children }) => <div style={{
290290
textAlign: 'center',
291291
gridColumn: 'span 2'
292292
}}>{children}</div>
293+
294+
export const tryFindOptionConfig = (option: keyof AppOptions) => {
295+
for (const group of Object.values(guiOptionsScheme)) {
296+
for (const optionConfig of group) {
297+
if (option in optionConfig) {
298+
return optionConfig[option]
299+
}
300+
}
301+
}
302+
303+
return null
304+
}

src/react/KeybindingsCustom.tsx

Lines changed: 74 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { useState } from 'react'
1+
import { useEffect, useState } from 'react'
22
import { contro as controEx } from '../controls'
3+
import { customCommandsConfig } from '../customCommands'
34
import { AwaitingInputOverlay, ButtonWithMatchesAlert } from './KeybindingsScreenApp'
45
import Button from './Button'
56
import PixelartIcon from './PixelartIcon'
67
import styles from './KeybindingsScreen.module.css'
7-
8+
import Input from './Input'
89

910
export default (
1011
{
@@ -35,47 +36,88 @@ export default (
3536
isPS: boolean | undefined
3637
}
3738
) => {
38-
const [, forceUpdate] = useState(false)
39+
type CustomCommand = {
40+
keys: string[],
41+
gamepad: string[]
42+
type: string
43+
inputs: any[]
44+
}
45+
// todo need to save custom actions to localstorage in the upper component and pass it down parsed to keybindings with config inputs
46+
const [customConfig, setCustomConfig] = useState([] as CustomCommand[]/* userConfig.custom */)
47+
useEffect(() => {
48+
localStorage.setItem('customConfig', JSON.stringify(customConfig))
49+
// userConfig.custom = customConfig
50+
}, [customConfig])
3951

40-
const addNewChatCommand = (e) => {
41-
let chatCommands
42-
if (userConfig.custom) {
43-
chatCommands = Object.keys(userConfig.custom)
44-
} else {
45-
chatCommands = [] as string[]
46-
}
47-
let commandName = 'chat_command_' + generateCode()
48-
while (chatCommands.includes(commandName)) {
49-
commandName = 'chat_command_' + generateCode()
50-
}
51-
setBinding({}, 'custom', commandName, 0)
52-
forceUpdate(prev => !prev)
52+
const addNewCommand = (type) => {
53+
setCustomConfig(prev => [...prev, {
54+
keys: [],
55+
gamepad: [],
56+
type,
57+
inputs: []
58+
}])
59+
60+
// setBinding({}, 'custom', commandName, 0)
61+
}
62+
63+
const setInputValue = (indexOption, indexInput, value) => {
64+
setCustomConfig(prev => {
65+
const newConfig = [...prev]
66+
newConfig[indexOption].inputs[indexInput] = value
67+
return newConfig
68+
})
5369
}
5470

5571
return <>
5672
<div className={styles.group}>
73+
{Object.entries(customCommandsConfig).map(([group, { input }]) => (
74+
<><div key={group} className={styles['group-category']}>{group}</div>
75+
{customConfig.filter(x => x.type === group).map(({ keys, gamepad, inputs }, indexOption) => {
76+
return <div key={indexOption}>
77+
{input.map((obj, indexInput) => {
78+
const config = typeof obj === 'function' ? obj(inputs) : obj
79+
if (!config) return null
80+
81+
return config.type === 'select'
82+
? <select key={indexInput} onChange={(e) => {
83+
setInputValue(indexOption, indexInput, e.target.value)
84+
}}>{config.options.map((option) => <option key={option} value={option}>{option}</option>)}</select>
85+
: <Input key={indexInput} placeholder={config.placeholder} value={inputs[indexInput]} onChange={(e) => setInputValue(indexOption, indexInput, e.target.value)} />
86+
})}
87+
</div>
88+
})}
89+
<Button
90+
onClick={() => addNewCommand(group)}
91+
icon={'pixelarticons:add-box'}
92+
style={{
93+
alignSelf: 'center'
94+
}}
95+
/>
96+
</>
97+
))}
5798
<div className={styles['group-category']}>Chat commands</div>
5899
{userConfig.custom &&
59100
Object.entries(userConfig.custom)
60-
.map(([action, { keys, gamepadButtons }]) => <ChatCommandBind
61-
key={`${action}`}
62-
group={'custom'}
63-
action={action}
64-
parseBindingName={parseBindingName}
65-
handleClick={handleClick}
66-
keys={keys}
67-
userConfig={userConfig}
68-
gamepadButtons={gamepadButtons}
69-
resetBinding={resetBinding}
70-
setActionName={setActionName}
71-
setGroupName={setGroupName}
72-
forceUpdate={forceUpdate}
73-
isPS={isPS}
74-
/>
101+
.map(([action, { keys, gamepadButtons }]) =>
102+
<ChatCommandBind
103+
key={`${action}`}
104+
group={'custom'}
105+
action={action}
106+
parseBindingName={parseBindingName}
107+
handleClick={handleClick}
108+
keys={keys}
109+
userConfig={userConfig}
110+
gamepadButtons={gamepadButtons}
111+
resetBinding={resetBinding}
112+
setActionName={setActionName}
113+
setGroupName={setGroupName}
114+
forceUpdate={forceUpdate}
115+
isPS={isPS}
116+
/>
75117
)}
76118

77119
<Button
78-
onClick={addNewChatCommand}
120+
onClick={addNewCommand}
79121
icon={'pixelarticons:add-box'}
80122
style={{
81123
alignSelf: 'center'
@@ -195,18 +237,3 @@ const ChatCommandBind = ({
195237
placeholder='Chat command' />
196238
</>
197239
}
198-
199-
const generateCode = () => {
200-
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
201-
const digits = '0123456789'
202-
203-
let code = ''
204-
for (let i = 0; i < 4; i++) {
205-
const randomLetter = letters.charAt(Math.floor(Math.random() * letters.length))
206-
const randomDigit = digits.charAt(Math.floor(Math.random() * digits.length))
207-
code += (i < 3) ? randomLetter : '_' + randomDigit
208-
}
209-
210-
return code
211-
}
212-

src/react/KeybindingsScreen.module.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
}
3333

3434
.actionName {
35-
flex-basis: 40%;
35+
flex-basis: 23%;
3636
flex-wrap: wrap;
37+
align-self: center;
38+
font-size: 13px;
3739
}
3840

3941
.undo-keyboard,
@@ -81,4 +83,4 @@
8183
display: block;
8284
height: 100%;
8385
flex-grow: 1;
84-
}
86+
}

src/react/OptionsGroup.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { options } from '../optionsStorage'
33
import { OptionsGroupType, guiOptionsScheme } from '../optionsGuiScheme'
44
import OptionsItems, { OptionMeta } from './OptionsItems'
55

6-
const optionValueToType = (optionValue: any, item: OptionMeta) => {
6+
export const optionValueToType = (optionValue: any, item: OptionMeta) => {
77
if (typeof optionValue === 'boolean' || item.values) return 'toggle'
88
if (typeof optionValue === 'number') return 'slider'
99
if (typeof optionValue === 'string') return 'element'

0 commit comments

Comments
 (0)