You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Are you reporting a bug? Use github issues for bug reports and feature requests. For general questions, please use https://discuss.yjs.dev/
Try to report your issue in the correct repository. Yjs consists of many modules. When in doubt, report it to https://github.com/yjs/yjs/issues/
Describe the bug
When undoing the combination of map set operation together with an operation that deletes that map from its parent array and then synchronizing, the result is that the other client ends up in a bad and different state than the client that did the undo.
That other client ends up with the changed property missing in the YMap instead of being changed back.
So 1 the other client (the one not doing the undo, but receiving the sync result afterwards) ends up in a bad state.
use stopCapturing on UndoManager in order to group the next 2 operations
change 1 of the 2 properties of the YMap
delete the YMap from the YArray
sync clients
call undo() on UndoManager
sync clients Expected:
YMap is added back to the YArray and has the old content again on both clients Actual:
The other client that is synchronized ends up in a bad state: the value that was changed using set in the YMap is missing there
Workaround
Issue can be kind of worked around by splitting up the operations in the undo manager by using stopCapturing unbetween. when the 2 operations are undone separately, the issue is not reproducible.
Code:
import{init}from'../testHelper.js'import*asYfrom'yjs'import*astfrom'lib0/testing'/** * @param {t.TestCase} tc */exportconsttestUndoMapSetAndDeleteFromArray=tc=>{const{ testConnector, array0, array1 }=init(tc,{users: 3})constundoManager=newY.UndoManager(array0)constmapInArray=newY.Map()mapInArray.set('untouchedProp','untouched prop value')consttoBeChangedPropKey='toBeChangedProp'mapInArray.set(toBeChangedPropKey,'before change')array0.push([mapInArray])testConnector.syncAll()// START UNDO BLOCKundoManager.stopCapturing()// Change some value in the YMapmapInArray.set(toBeChangedPropKey,'changed content')testConnector.syncAll()// undoManager.stopCapturing() // Workaround is to split up the undo and then doing undo twice// Delete the YMap from its parent Arrayarray0.delete(0,1)testConnector.syncAll()// Undo the set + deletion from parent in 1 undoundoManager.undo()// undoManager.undo() // Workaround is to split up the undo and then doing undo twicetestConnector.syncAll()t.assert(array0.toJSON()[0][toBeChangedPropKey]==='before change')// OKt.assert(array1.toJSON()[0][toBeChangedPropKey]==='before change')// Failing and undefined instead}
The text was updated successfully, but these errors were encountered:
Note: we also found another woraround
When using { ignoreRemoteMapChanges: true } in the UndoManager constructor, this issue is also not reproducible.
francisduvivier
changed the title
UndoManager: Undoing combination of map set and deleting that map from parent array results in 1 client with invalid map.
UndoManager: Undoing combination of Y.Map#set and deleting that Y.Map from parent Y.Array results in 1 client with invalid map
May 3, 2024
francisduvivier
changed the title
UndoManager: Undoing combination of Y.Map#set and deleting that Y.Map from parent Y.Array results in 1 client with invalid map
UndoManager: Undoing combination of Y.Map#set and deleting that Y.Map from parent Y.Array results in 1 client with invalid Y.Map
May 3, 2024
I also just hit this issue using XmlFragments and was doing some digging with the debugger before finding this issue.
UndoManager calls redoItem to remake the map and keys. When remaking the Item for 'before change', origin is set to the now-deleted item 'changed content', but parent correctly points to the new, re-created map.
Then the function Item.write serializes the items to sync remote clients, but Item.write only encodes parent if origin is null, so remote clients only see that the 'before change' key has an origin pointing to the deleted map key. As far as I can tell, it looks like remote clients are connecting the recreated key to the deleted map, instead of the recreated map.
Checklist
Describe the bug
When undoing the combination of map set operation together with an operation that deletes that map from its parent array and then synchronizing, the result is that the other client ends up in a bad and different state than the client that did the undo.
That other client ends up with the changed property missing in the YMap instead of being changed back.
So 1 the other client (the one not doing the undo, but receiving the sync result afterwards) ends up in a bad state.
To Reproduce
Run the test code below (also on my fork in undo-redo.bug-reproduction.spec.js)
Reproduction Steps Summary:
Expected:
YMap is added back to the YArray and has the old content again on both clients
Actual:
The other client that is synchronized ends up in a bad state: the value that was changed using set in the YMap is missing there
Workaround
Issue can be kind of worked around by splitting up the operations in the undo manager by using stopCapturing unbetween. when the 2 operations are undone separately, the issue is not reproducible.
Code:
The text was updated successfully, but these errors were encountered: