From 5d862477cd307b8c9e396e56524d76ef2b0698fa Mon Sep 17 00:00:00 2001 From: Noel Levy Date: Mon, 19 Jun 2023 11:31:45 -0700 Subject: [PATCH 1/2] invalidate cached path when changing currentTarget of event fixes #544 --- src/utils/Transaction.js | 1 + tests/y-map.tests.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 299835ee..a7b23ff8 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -287,6 +287,7 @@ const cleanupTransactions = (transactionCleanups, i) => { events .forEach(event => { event.currentTarget = type + event._path = null }) // sort events by path length so that top-level events are fired first. events diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index e12d55c9..3356afc1 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -337,6 +337,34 @@ export const testObserversUsingObservedeep = tc => { compare(users) } +/** + * @param {t.TestCase} tc + */ +export const testPathsOfSiblingEvents = tc => { + const { users, map0 } = init(tc, { users: 2 }) + /** + * @type {Array>} + */ + const pathes = [] + let calls = 0 + const doc = users[0] + map0.set('map', new Y.Map()) + map0.get('map').set('text1', new Y.Text('initial')) + map0.observeDeep(events => { + events.forEach(event => { + pathes.push(event.path) + }) + calls++ + }) + doc.transact(() => { + map0.get('map').get('text1').insert(0, 'post-') + map0.get('map').set('text2', new Y.Text('new')) + }) + t.assert(calls === 1) + t.compare(pathes, [['map'], ['map', 'text1']]) + compare(users) +} + // TODO: Test events in Y.Map /** * @param {Object} is From 942c8a267be05b0b1156f9be8d62347ae33dcad2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 22 Jun 2023 17:46:49 +0200 Subject: [PATCH 2/2] remove duplicate Transaction.callAll logic --- src/utils/Transaction.js | 47 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index a7b23ff8..35940595 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -275,31 +275,30 @@ const cleanupTransactions = (transactionCleanups, i) => { ) fs.push(() => { // deep observe events - transaction.changedParentTypes.forEach((events, type) => - fs.push(() => { - // We need to think about the possibility that the user transforms the - // Y.Doc in the event. - if (type._item === null || !type._item.deleted) { - events = events - .filter(event => - event.target._item === null || !event.target._item.deleted - ) - events - .forEach(event => { - event.currentTarget = type - event._path = null - }) - // sort events by path length so that top-level events are fired first. - events - .sort((event1, event2) => event1.path.length - event2.path.length) - // We don't need to check for events.length - // because we know it has at least one element - callEventHandlerListeners(type._dEH, events, transaction) - } - }) - ) - fs.push(() => doc.emit('afterTransaction', [transaction, doc])) + transaction.changedParentTypes.forEach((events, type) => { + // We need to think about the possibility that the user transforms the + // Y.Doc in the event. + if (type._dEH.l.length > 0 && (type._item === null || !type._item.deleted)) { + events = events + .filter(event => + event.target._item === null || !event.target._item.deleted + ) + events + .forEach(event => { + event.currentTarget = type + // path is relative to the current target + event._path = null + }) + // sort events by path length so that top-level events are fired first. + events + .sort((event1, event2) => event1.path.length - event2.path.length) + // We don't need to check for events.length + // because we know it has at least one element + callEventHandlerListeners(type._dEH, events, transaction) + } + }) }) + fs.push(() => doc.emit('afterTransaction', [transaction, doc])) callAll(fs, []) if (transaction._needFormattingCleanup) { cleanupYTextAfterTransaction(transaction)