I’m building a task management product which uses Y.js only for task titles (screenshot with dummy data below). Each task title has its own Y.Doc
. Other fields (like priority and due date) use a different system.
I’m building out undo/redo now. The way it works is there’s a log of actions and for each action I generate an inverted action. For example if you update priority from Low to High then I’ll generate an inverted action that switches it back to Low. I’m hoping I can generate inverted actions for Y.js task titles as well.
I’ve spent some time looking at Y.UndoManager’s code. It’s calling a lot of internal functions so it’s not clear to me that it’ll be simple to reimplement in my code. So here’s what I have so far:
function getInvertedTitleUpdate(oldTitle, titleUpdate) {
const yDoc = new Y.Doc();
Y.applyUpdateV2(yDoc, oldTitle);
const yUndoManager = new Y.UndoManager(yDoc.getXmlFragment("doc"));
Y.applyUpdateV2(yDoc, titleUpdate);
const invertedTitleUpdates = [];
yDoc.on("updateV2", (invertedTitleUpdate) => {
invertedTitleUpdates.push(invertedTitleUpdate);
});
yUndoManager.undo();
yDoc.destroy();
return Y.mergeUpdatesV2(invertedTitleUpdates);
}
This is working well for simple updates. But I’ve found at least one bug:
- Type “quick undo test”
- Replace “undo” with “redo”
- Now you have “quick redo test”
- Hit cmd-z, this gives me “quick undo test” (good)
- Hit cmd-z, this gives me “undo” (uh oh! expected an empty string “”)
I haven’t spent time trying to deeply understand the Y.js data format so I don’t understand what’s happening here. Some questions:
- Is it possible to implement a
getInvertedTitleUpdate(oldTitle, titleUpdate)
function? What’s the implementation if so? - How does
Y.UndoManager
avoid the bug I’ve observed?