3.2 KiB
Plan: Issue #89 — Undo/Redo for Character Changes
Summary
Implement an undo/redo stack in CharacterViewModel so that users can revert accidental character changes. The undo/redo buttons will be added to the top app bar. The stack tracks the last 20 character snapshots. Auto-save continues to save the current (post-undo/redo) state only.
Implementation Steps
Step 1: Create UndoRedoManager utility class
File: sharedUI/src/commonMain/kotlin/org/shahondin1624/viewmodel/UndoRedoManager.kt
Create a generic UndoRedoManager<T> class that:
- Maintains an undo stack (max 20 entries) and a redo stack
- Exposes
canUndo: StateFlow<Boolean>andcanRedo: StateFlow<Boolean> - Methods:
pushState(state: T),undo(current: T): T?,redo(current: T): T?,clear() - When a new state is pushed, the redo stack is cleared
- When the undo stack exceeds 20 entries, the oldest entry is dropped
Step 2: Integrate UndoRedoManager into CharacterViewModel
File: sharedUI/src/commonMain/kotlin/org/shahondin1624/viewmodel/CharacterViewModel.kt
- Add an
UndoRedoManager<ShadowrunCharacter>instance - In
updateCharacter()andsetCharacter(): push the OLD state onto the undo stack before applying the new state - Add
undo()method: callsundoRedoManager.undo(current), sets the result as current character (without pushing to undo stack) - Add
redo()method: callsundoRedoManager.redo(current), sets the result as current character (without pushing to undo stack) - Expose
canUndo: StateFlow<Boolean>andcanRedo: StateFlow<Boolean>from the manager - Auto-save debounce continues to work normally -- it saves whatever the current state is
Step 3: Add undo/redo buttons to the top app bar
File: sharedUI/src/commonMain/kotlin/org/shahondin1624/App.kt
- Add Undo and Redo
IconButtons in theactionsblock ofTopAppBar, before the save status indicator - Use
Icons.AutoMirrored.Filled.UndoandIcons.AutoMirrored.Filled.Redo(orIcons.Default.Undo/Icons.Default.Redo) - Buttons are enabled/disabled based on
canUndo/canRedostate - Only show on the CHARACTER_SHEET route (not on creation or settings)
Step 4: Add string resources
File: sharedUI/src/commonMain/composeResources/values/strings.xml
- Add
undo_content_desc= "Undo" - Add
redo_content_desc= "Redo"
Step 5: Add test tags
File: sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt
- Add
UNDO_BUTTON = "undo_button" - Add
REDO_BUTTON = "redo_button"
Step 6: Write unit tests for UndoRedoManager
File: sharedUI/src/commonTest/kotlin/org/shahondin1624/viewmodel/UndoRedoManagerTest.kt
Test cases:
- Initial state: canUndo=false, canRedo=false
- Push state -> canUndo=true, canRedo=false
- Undo returns previous state, canRedo=true
- Redo returns the state that was undone
- Push after undo clears redo stack
- Stack limited to 20 entries
- Clear resets everything
AC Verification Checklist
- Undo button available in the top bar (on character sheet route)
- At least the last 10 changes can be undone (we support 20)
- Redo is available after undo
- Auto-save works with the undo stack (saves current state, not undo history)