software-mansion / software-mansion/react-native-enriched-html
[Feature request] Imperative undo/redo API for iOS & Android (with canUndo/canRedo state)
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 1.4k
- Forks
- 66
- Avg merge
- 4d 19h
- Merged PRs (30d)
- 11
Description
[Feature request] Imperative undo/redo API for iOS & Android (with canUndo/canRedo state)
Is your feature request related to a problem? Please describe.
I'm building a note-taking app on EnrichedTextInput and there is currently no way to offer undo/redo toolbar buttons on mobile:
EnrichedTextInputInstanceexposes noundo()/redo()methods.OnChangeStateEventhas nocanUndo/canRedoflags, so a toolbar can't enable/disable the buttons.
Web already supports undo/redo since #622 via @tiptap/extension-history, but only through keyboard shortcuts — there is no imperative API on any platform, and mobile has no history at all. #429 asked for this earlier but was closed for inactivity, so I'd like to pick it up properly.
Today the only workaround is an app-level snapshot stack (debounced getHTML() + setValue() restore), which loses selection, fights the keyboard, and duplicates work the platform can do natively.
Describe the solution you'd like
I'd like to contribute this myself and want to align on the API before opening PRs. Proposal:
1. Imperative methods on EnrichedTextInputInstance:
/** Undoes the last user edit (typing, formatting, link/image insertion). No-op when history is empty. */
undo: () => void;
/** Re-applies the most recently undone edit. No-op when there is nothing to redo. */
redo: () => void;
2. History state on OnChangeStateEvent:
canUndo: boolean;
canRedo: boolean;
Flat booleans rather than the { isActive, isConflicting, isBlocking } shape, since undo isn't a text style — alignment: string is the existing flat-field precedent. Happy to emit these via a separate event instead if you prefer to keep OnChangeStateEvent style-only.
3. Semantics:
- All user-originated edits are undoable: typing,
toggle*formatting,setLink/setImage/setMention. - Programmatic
setValue()clears the history stack — loading content into the editor must not be undoable into an empty editor.
4. Platform notes:
- iOS: plan to build on the text view's native
NSUndoManager, so shake-to-undo, the three-finger gestures, and hardware-keyboard Cmd+Z stay consistent with the imperative API for free. If the editor's attributed-string mutations turn out not to register cleanly with the undo manager, fall back to a snapshot/command stack. - Android: no built-in undo manager on
EditText, so this would be a Kotlin-side history stack. - Web: thin wiring of the same API onto the already-merged TipTap history (
editor.commands.undo()/editor.can().undo()), closing the parity gap with the shortcuts added in #622.
Delivery: split by platform following the repo's existing pattern (feat(ios): …, then feat(android): …), each with Maestro flows (undo after typing, undo after formatting, setValue reset) and example-app toolbar buttons.
Describe alternatives you've considered
- App-level snapshot undo (
getHTML()+setValue()): works but loses selection/caret, creates large string churn on every keystroke, and can't integrate with OS-level undo gestures. - Keyboard-shortcut-only parity with web: hardware Cmd+Z already partially works on iOS, but on-screen toolbar buttons are the primary mobile UX and need both the imperative calls and the
canUndo/canRedostate. - Exposing only methods without state flags: forces apps to track dirty state themselves and still mis-renders button enablement after undo-to-initial-state.
Additional context
- Related: #429 (closed for inactivity), #622 (web undo/redo, merged).
- I have the implementation scoped and am ready to start with the iOS PR once the API shape is agreed.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the EnrichedTextInputInstance and OnChangeStateEvent APIs, then inspect the web TipTap history wiring from #622 and the iOS and Android implementations. Done means an agreed platform-split API with undo/redo state, Maestro flows for typing, formatting, and setValue reset, plus example-app toolbar buttons.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, ios, kotlin, react-native, typescript
- Domain
- api, mobile, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100