Merge pull request #34714 from overleaf/mj-tracked-change-merges

[overleaf-editor-core+ranges-tracker] Pick lower timestamp when merging tracking info

GitOrigin-RevId: 5b1c8d7c719003df4506014c381f2a525d0a7ff4
This commit is contained in:
Mathias Jakobsen
2026-06-19 08:07:27 +00:00
committed by Copybot
parent 7ee385fe45
commit ef05288f81
6 changed files with 257 additions and 35 deletions
+16 -1
View File
@@ -420,7 +420,7 @@ class RangesTracker {
offset = opStart - changeStart
change.op.i =
change.op.i.slice(0, offset) + op.i + change.op.i.slice(offset)
change.metadata.ts = metadata.ts
change.metadata.ts = pickTimestamp(change.metadata, metadata)
alreadyMerged = true
movedChanges.push(change)
} else if (opStart <= changeStart) {
@@ -728,6 +728,10 @@ class RangesTracker {
) {
removeChanges.push(change)
previousChange.op.i += change.op.i
previousChange.metadata.ts = pickTimestamp(
previousChange.metadata,
change.metadata
)
movedChanges.push(previousChange)
}
} else if (
@@ -790,4 +794,15 @@ class RangesTracker {
}
}
function pickTimestamp(oldMetadata, newMetadata) {
// Make sure null values don't get treated as 1970-01-01 dates in the
// comparison below.
if (oldMetadata.ts == null) return newMetadata.ts
if (newMetadata.ts == null) return oldMetadata.ts
return new Date(oldMetadata.ts) < new Date(newMetadata.ts)
? oldMetadata.ts
: newMetadata.ts
}
module.exports = RangesTracker
@@ -222,4 +222,114 @@ describe('RangesTracker', function () {
)
})
})
describe('merging tracked inserts from the same user', function () {
beforeEach(function () {
this.comments = []
})
it('keeps the earlier timestamp when the existing insert is older', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'foo' },
metadata: { user_id: 'user-1', ts: '2024-01-01T00:00:00.000Z' },
},
],
this.comments
)
this.rangesTracker.track_changes = true
this.rangesTracker.applyOp(
{ p: 13, i: 'bar' },
{ user_id: 'user-1', ts: new Date('2024-06-01T00:00:00.000Z') }
)
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(change.op).to.deep.equal({ p: 10, i: 'foobar' })
expect(change.metadata.user_id).to.equal('user-1')
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
it('keeps the earlier timestamp when the incoming insert is older', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'foo' },
metadata: { user_id: 'user-1', ts: '2024-06-01T00:00:00.000Z' },
},
],
this.comments
)
this.rangesTracker.track_changes = true
this.rangesTracker.applyOp(
{ p: 13, i: 'bar' },
{ user_id: 'user-1', ts: new Date('2024-01-01T00:00:00.000Z') }
)
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(change.op).to.deep.equal({ p: 10, i: 'foobar' })
expect(change.metadata.user_id).to.equal('user-1')
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
it('uses the defined timestamp when the existing insert has a null timestamp', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'foo' },
metadata: { user_id: 'user-1', ts: null },
},
],
this.comments
)
this.rangesTracker.track_changes = true
this.rangesTracker.applyOp(
{ p: 13, i: 'bar' },
{ user_id: 'user-1', ts: new Date('2024-01-01T00:00:00.000Z') }
)
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
it('keeps the earlier timestamp when re-merging inserts after a deletion', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'aaa' },
metadata: { user_id: 'user-1', ts: '2024-03-01T00:00:00.000Z' },
},
{
id: 'id2',
op: { p: 13, i: 'bb' },
metadata: { user_id: 'user-2', ts: '2024-02-01T00:00:00.000Z' },
},
{
id: 'id3',
op: { p: 15, i: 'ccc' },
metadata: { user_id: 'user-1', ts: '2024-01-01T00:00:00.000Z' },
},
],
this.comments
)
this.rangesTracker.applyOp({ p: 13, d: 'bb' })
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(change.op).to.deep.equal({ p: 10, i: 'aaaccc' })
expect(change.metadata.user_id).to.equal('user-1')
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
})
})