Skip to content

Commit 9910bcd

Browse files
committed
feat(editor2): remember modified skill levels
1 parent abe9fc1 commit 9910bcd

File tree

3 files changed

+71
-21
lines changed

3 files changed

+71
-21
lines changed

src/components/editor2/editor-state.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
getDefaultStore,
66
useAtom,
77
} from 'jotai'
8-
import { atomWithStorage, splitAtom } from 'jotai/utils'
8+
import { atomFamily, atomWithStorage, splitAtom } from 'jotai/utils'
99
import { noop } from 'lodash-es'
1010
import { useMemo } from 'react'
1111
import { SetRequired, Simplify } from 'type-fest'
@@ -237,6 +237,11 @@ const editorVisibleEntityErrorsAtom = atom((get) =>
237237
get(editorErrorsVisibleAtom) ? get(editorEntityErrorsAtom) : undefined,
238238
)
239239

240+
// this atom will cause some memory leak but generally not a big deal
241+
const skillLevelOverridesAtom = atomFamily((id: string) =>
242+
atom<Record<number, number>>({}),
243+
)
244+
240245
export const editorAtoms = {
241246
editor: editorAtom,
242247
operation: operationAtom,
@@ -260,13 +265,39 @@ export const editorAtoms = {
260265
activeActionIdAtom: atom<string | undefined>(undefined),
261266
sourceEditorIsOpen: atom(false),
262267
selectorPanelMode: atom<'operator' | 'map'>('operator'),
268+
// this atom will cause some memory leak as it does not clean up until the editor is reset,
269+
// but generally it's not a big deal
270+
skillLevelOverrides: skillLevelOverridesAtom,
263271

264272
// validation
265273
globalErrors: editorGlobalErrorsAtom,
266274
entityErrors: editorEntityErrorsAtom,
267275
errorsVisible: editorErrorsVisibleAtom,
268276
visibleGlobalErrors: editorVisibleGlobalErrorsAtom,
269277
visibleEntityErrors: editorVisibleEntityErrorsAtom,
278+
279+
reset: atom(
280+
null,
281+
(get, set, editorState: EditorState = defaultEditorState) => {
282+
set(editorAtom, editorState)
283+
set(editorGlobalErrorsAtom, [])
284+
set(editorEntityErrorsAtom, {})
285+
286+
skillLevelOverridesAtom.setShouldRemove(() => true)
287+
skillLevelOverridesAtom.setShouldRemove(null)
288+
const setSkillLevel = (operator: EditorOperator) => {
289+
if (operator.skill && operator.requirements?.skillLevel) {
290+
set(skillLevelOverridesAtom(operator.id), {
291+
[operator.skill]: operator.requirements.skillLevel,
292+
})
293+
}
294+
}
295+
editorState.operation.opers.forEach(setSkillLevel)
296+
editorState.operation.groups.forEach((group) =>
297+
group.opers.forEach(setSkillLevel),
298+
)
299+
},
300+
),
270301
}
271302

272303
export const historyAtom = createHistoryAtom(

src/components/editor2/operator/OperatorItem.tsx

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { Popover2 } from '@blueprintjs/popover2'
1111

1212
import clsx from 'clsx'
13-
import { useSetAtom } from 'jotai'
13+
import { useAtom, useSetAtom } from 'jotai'
1414
import { clamp } from 'lodash-es'
1515
import { FC, memo } from 'react'
1616

@@ -34,7 +34,7 @@ import { SortableItemProps } from '../../dnd'
3434
import { DetailedSelect } from '../../editor/DetailedSelect'
3535
import { NumericInput2 } from '../../editor/NumericInput2'
3636
import { OperatorAvatar } from '../../editor/operator/EditorOperator'
37-
import { EditorOperator, useEdit } from '../editor-state'
37+
import { EditorOperator, editorAtoms, useEdit } from '../editor-state'
3838
import { editorFavOperatorsAtom } from '../reconciliation'
3939

4040
interface OperatorItemProps extends Partial<SortableItemProps> {
@@ -64,17 +64,16 @@ export const OperatorItem: FC<OperatorItemProps> = memo(
6464
}) => {
6565
const t = useTranslation()
6666
const edit = useEdit()
67+
const [skillLevels, setSkillLevels] = useAtom(
68+
editorAtoms.skillLevelOverrides(operator.id),
69+
)
6770
const setFavOperators = useSetAtom(editorFavOperatorsAtom)
6871
const info = OPERATORS.find(({ name }) => name === operator.name)
6972
const skillCount = info ? getSkillCount(info) : 3
7073
const requirements = withDefaultRequirements(
7174
operator.requirements,
7275
info?.rarity,
7376
)
74-
const detailedSkills = Array.from({ length: skillCount }, (_, index) => ({
75-
available: index <= requirements.elite,
76-
defaultLevel: getDefaultRequirements(info?.rarity).skillLevel,
77-
}))
7877
const controlsEnabled = !onOverlay && !isDragging && !isSorting
7978

8079
return (
@@ -307,13 +306,16 @@ export const OperatorItem: FC<OperatorItemProps> = memo(
307306

308307
<ul className="w-8 grid grid-rows-4 gap-1 ml-1 mt-1">
309308
{controlsEnabled &&
310-
detailedSkills.map(({ defaultLevel, available }, index) => {
309+
Array.from({ length: skillCount }, (_, index) => {
310+
const available = index <= requirements.elite
311311
const skillNumber = index + 1
312312
const selected = operator.skill === skillNumber
313+
const maxSkillLevel = requirements.elite === 2 ? 10 : 7
313314
const skillLevel = selected
314315
? requirements.skillLevel
315-
: defaultLevel
316-
const maxLevel = requirements.elite === 2 ? 10 : 7
316+
: skillLevels[skillNumber] ??
317+
getDefaultRequirements(info?.rarity).skillLevel
318+
317319
return (
318320
<li
319321
key={index}
@@ -343,7 +345,15 @@ export const OperatorItem: FC<OperatorItemProps> = memo(
343345
onFocus={() => {
344346
if (operator.skill !== skillNumber) {
345347
edit(() => {
346-
onChange?.({ ...operator, skill: skillNumber })
348+
onChange?.({
349+
...operator,
350+
skill: skillNumber,
351+
requirements: {
352+
...operator.requirements,
353+
// override with the current skill level
354+
skillLevel,
355+
},
356+
})
347357
return {
348358
action: 'set-operator-skill',
349359
desc: i18n.actions.editor2.set_operator_skill,
@@ -362,8 +372,12 @@ export const OperatorItem: FC<OperatorItemProps> = memo(
362372
if (newLevel === 0) {
363373
newLevel = 10
364374
}
365-
newLevel = clamp(newLevel, 1, maxLevel)
375+
newLevel = clamp(newLevel, 1, maxSkillLevel)
366376

377+
setSkillLevels((prev) => ({
378+
...prev,
379+
[skillNumber]: newLevel,
380+
}))
367381
onChange?.({
368382
...operator,
369383
requirements: {
@@ -381,15 +395,20 @@ export const OperatorItem: FC<OperatorItemProps> = memo(
381395
onWheelFocused={(e) => {
382396
e.preventDefault()
383397
edit(() => {
398+
const newLevel = clamp(
399+
requirements.skillLevel + (e.deltaY > 0 ? -1 : 1),
400+
1,
401+
maxSkillLevel,
402+
)
403+
setSkillLevels((prev) => ({
404+
...prev,
405+
[skillNumber]: newLevel,
406+
}))
384407
onChange?.({
385408
...operator,
386409
requirements: {
387410
...operator.requirements,
388-
skillLevel: clamp(
389-
requirements.skillLevel + (e.deltaY > 0 ? -1 : 1),
390-
1,
391-
maxLevel,
392-
),
411+
skillLevel: newLevel,
393412
},
394413
})
395414
return {

src/pages/editor.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const EditorPage = withSuspensable(() => {
3939
revalidateOnReconnect: false,
4040
}).data
4141
const t = useTranslation()
42-
const setEditorState = useSetAtom(editorAtoms.editor)
42+
const resetEditor = useSetAtom(editorAtoms.reset)
4343

4444
if (process.env.NODE_ENV === 'development') {
4545
// eslint-disable-next-line react-hooks/rules-of-hooks
@@ -48,7 +48,7 @@ export const EditorPage = withSuspensable(() => {
4848

4949
useLayoutEffect(() => {
5050
if (apiOperation) {
51-
setEditorState({
51+
resetEditor({
5252
operation: toEditorOperation(
5353
operationLooseSchema.parse(JSON.parse(apiOperation.content)),
5454
),
@@ -60,9 +60,9 @@ export const EditorPage = withSuspensable(() => {
6060
},
6161
})
6262
} else {
63-
setEditorState(defaultEditorState)
63+
resetEditor(defaultEditorState)
6464
}
65-
}, [apiOperation, setEditorState])
65+
}, [apiOperation, resetEditor])
6666

6767
const handleSubmit = useAtomCallback(
6868
useCallback(

0 commit comments

Comments
 (0)