-
-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add undo-stack on a per map basis #22
Conversation
Here are the replies to your questions from the issue:
If you rebase, now it will be an empty string when you "save" it.
I think that makes the most sense, yes. If you don't hit restore before clicking "new map", we should wipe the undo state.
Yes that's fine. The docs page doesn't have those confirmation dialogs but they are in the game.
There are no tests 🪦 If you write a function (not React) that you want to test, feel free to add a test. Otherwise we are living in a brave world here. |
hera/editor/lib/updateUndoStack.tsx
Outdated
export const UNDO_STACK_KEY = (id: string | undefined) => | ||
`map-editor-undo-stack-${id ? `${id}` : 'fallback'}`; | ||
|
||
export const UNDO_STACK_INDEX_KEY = (id: string | undefined) => | ||
`map-editor-undo-stack-index-${id ? `${id}` : 'fallback'}`; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these are functions they should be getUndoStackKey
and getUndoStackIndexKey
.
hera/editor/Types.tsx
Outdated
) => void; | ||
|
||
type MapSaveType = 'New' | 'Update' | 'Disk' | 'Export'; | ||
export type MapCreateVariables = Readonly<{ | ||
effects: Effects; | ||
id: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this.
When you rebase, you'll be able to find this: https://github.com/nkzw-tech/athena-crisis/blob/main/docs/content/examples/map-editor.tsx#L93
Let's change that line to set the id to the same as slug
, that means you'll always have a unique id to work with.
hera/editor/MapEditor.tsx
Outdated
export function decodeUndoStack( | ||
encodedUndoStack: string | null, | ||
): UndoStack | undefined { | ||
if (!encodedUndoStack) { | ||
return undefined; | ||
} | ||
|
||
try { | ||
return JSON.parse(encodedUndoStack).map( | ||
([key, value]: [string, PlainMap]) => [key, MapData.fromObject(value)], | ||
); | ||
} catch { | ||
return undefined; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why return undefined
instead of null
?
hera/editor/MapEditor.tsx
Outdated
} | ||
|
||
const undoStackIndex = getUndoStackIndex(mapObject?.id); | ||
const map = undoStack[undoStackIndex ?? -1][1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if this ends up being -1
? It will crash, right?
Hey @tjamesmac, thank you so much for your contributions! I left a few comments, would you mind taking a look?
I think this is ok actually, that's the whole point of replacing the "restore" feature with this one, as long as the |
- change stack keys - remove id from MapCreateVariables type - use null instead of undefined - use array.length - 1 to calc last index
Hey @cpojer, thanks for the quick review and sorry for the slowish response. Busy weekend! I've made your suggestions and added a few more fixes for some edge cases. Would appreciate another review! Also thanks for open sourcing Athena crisis :) |
@@ -212,7 +261,8 @@ export default function MapEditor({ | |||
(size = new SizeVector(random(10, 15), random(10, 15))): MapData => { | |||
return withHumanPlayer( | |||
mapObject | |||
? MapData.fromJSON(mapObject.state)! | |||
? getInitialMapFromUndoStack(mapObject?.id)?.map ?? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this restores the state from the correct position in the undostack when loading the map from the url
hera/editor/MapEditor.tsx
Outdated
useEffect(() => { | ||
if (editor.undoStack) { | ||
const stack = editor.undoStack.map(([key, value]) => [ | ||
key, | ||
value.toJSON(), | ||
]); | ||
Storage.setItem(getUndoStackKey(mapObject?.id), JSON.stringify(stack)); | ||
} | ||
}, [editor.undoStack, mapObject?.id]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved the stack saving to localstorage from updateUndoStack to here because it was out of sync by one
docs/content/examples/map-editor.tsx
Outdated
@@ -80,6 +80,8 @@ export default function MapEditorExample() { | |||
|
|||
const handleMapUpdate = useCallback( | |||
(variables: MapCreateVariables | MapUpdateVariables) => { | |||
const nameToSlug = toSlug(variables.mapName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const nameToSlug = toSlug(variables.mapName); | |
const slug = toSlug(variables.mapName); |
docs/content/examples/map-editor.tsx
Outdated
@@ -90,9 +92,9 @@ export default function MapEditorExample() { | |||
username: viewer.username, | |||
}, | |||
effects: JSON.stringify(encodeEffects(variables.effects)), | |||
id: 'id' in variables ? variables.id : '', | |||
id: 'id' in variables ? variables.id : nameToSlug, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
id: 'id' in variables ? variables.id : nameToSlug, | |
id: 'id' in variables ? variables.id : slug, |
docs/content/examples/map-editor.tsx
Outdated
name: variables.mapName, | ||
slug: toSlug(variables.mapName), | ||
slug: nameToSlug, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slug: nameToSlug, | |
slug, |
hera/editor/lib/updateUndoStack.tsx
Outdated
export const getUndoStackKey = (id: string | undefined) => | ||
`map-editor-undo-stack-${id ? `${id}` : 'fallback'}`; | ||
|
||
export const getUndoStackIndexKey = (id: string | undefined) => | ||
`map-editor-undo-stack-index-${id ? `${id}` : 'fallback'}`; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a suggestion, curious what you think: From reading the code it's not obvious why the id
is passed. What do you think about calling these getUndoStackKeyFor(id)
and getUndoStackIndexKeyFor(id)
?
Sorry, didn't come to me earlier since they were uppercased and that confused me 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah, not a problem at all. Definitely think adding the For
makes the intent a little clearer
hera/editor/MapEditor.tsx
Outdated
@@ -319,40 +376,56 @@ export default function MapEditor({ | |||
const [previousState, setPreviousState] = | |||
useState<PreviousMapEditorState | null>(() => { | |||
try { | |||
const effects = Storage.getItem(EFFECTS_KEY) || ''; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this one empty line. There are too many empty lines here :D
This is looking really solid! I think there are only two more considerations before it's ready:
Just note, we should probably not immediately update undo every time an effect changes since a user can type into the textarea to update the text a character says and it would probably be slow and not fun to add each character to the undo stack. |
e7d0373
to
e7114fa
Compare
Hey thanks, got a bit busier than I expected. Will look at shifting the effects into the stack when I get chance |
- nameToSlug -> slug - getUndoStackKey -> getUndoStackKeyFor - getUndoStackKeyIndex -> getUndoStackKeyIndexFor - remove empty line
Hey @cpojer , I have a couple of questions!
How do you feel about the previous state (if present) just being restored straight off a reload instead of hitting restore to get back to the previous state? If that's okay I think I can check when getting the initial map state whether I should restore from:
Does this mean move the effects into |
I prefer not to do this. In the game, opening the map editor should start you off with a fresh map. I mostly just want undo to work properly in that empty state so that we can make the "Restore Map" button more prominent in the editor in a follow-up. That way we give control to the user.
I think we have to change undoEntry to take |
Closing due to inactivity. Happy to reopen if it's being picked up again. |
Description
Hoping to resolve #3
Adds the ability to:
Todo
Would appreciate any feedback and whether this has hit the mark! :)