Files
ShadowrunCharSheet/.plan/issue-89-undo-redo.md
T

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> and canRedo: 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() and setCharacter(): push the OLD state onto the undo stack before applying the new state
  • Add undo() method: calls undoRedoManager.undo(current), sets the result as current character (without pushing to undo stack)
  • Add redo() method: calls undoRedoManager.redo(current), sets the result as current character (without pushing to undo stack)
  • Expose canUndo: StateFlow<Boolean> and canRedo: 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 the actions block of TopAppBar, before the save status indicator
  • Use Icons.AutoMirrored.Filled.Undo and Icons.AutoMirrored.Filled.Redo (or Icons.Default.Undo/Icons.Default.Redo)
  • Buttons are enabled/disabled based on canUndo/canRedo state
  • 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

  1. Undo button available in the top bar (on character sheet route)
  2. At least the last 10 changes can be undone (we support 20)
  3. Redo is available after undo
  4. Auto-save works with the undo stack (saves current state, not undo history)