@manuscripts/track-changes-plugin 0.0.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -45
- package/dist/actions.d.ts +8 -1
- package/dist/index.cjs +1591 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +25 -13
- package/dist/index.js +173 -144
- package/dist/plugin.d.ts +2 -2
- package/dist/track/deleteNode.d.ts +1 -1
- package/dist/track/mergeNode.d.ts +1 -1
- package/dist/track/node-utils.d.ts +4 -11
- package/dist/track/steps/deleteAndMergeSplitNodes.d.ts +1 -1
- package/dist/track/steps/setFragmentAsInserted.d.ts +1 -1
- package/dist/track/steps/track-utils.d.ts +0 -1
- package/dist/track/trackTransaction.d.ts +2 -2
- package/dist/types/change.d.ts +4 -1
- package/package.json +17 -21
package/dist/index.js
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { PluginKey, Plugin } from 'prosemirror-state';
|
|
2
|
+
import debug from 'debug';
|
|
3
|
+
import { liftTarget, canJoin, Mapping, ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform';
|
|
4
|
+
import { Fragment, Slice } from 'prosemirror-model';
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var prosemirrorState = require('prosemirror-state');
|
|
6
|
-
var debug = require('debug');
|
|
7
|
-
var prosemirrorTransform = require('prosemirror-transform');
|
|
8
|
-
var prosemirrorModel = require('prosemirror-model');
|
|
9
|
-
|
|
10
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
11
|
-
|
|
12
|
-
var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
|
|
13
|
-
|
|
14
|
-
exports.TrackChangesAction = void 0;
|
|
6
|
+
var TrackChangesAction;
|
|
15
7
|
(function (TrackChangesAction) {
|
|
16
8
|
TrackChangesAction["skipTrack"] = "track-changes-skip-tracking";
|
|
17
9
|
TrackChangesAction["setUserID"] = "track-changes-set-user-id";
|
|
@@ -20,7 +12,7 @@ exports.TrackChangesAction = void 0;
|
|
|
20
12
|
TrackChangesAction["updateChanges"] = "track-changes-update-changes";
|
|
21
13
|
TrackChangesAction["refreshChanges"] = "track-changes-refresh-changes";
|
|
22
14
|
TrackChangesAction["applyAndRemoveChanges"] = "track-changes-apply-remove-changes";
|
|
23
|
-
})(
|
|
15
|
+
})(TrackChangesAction || (TrackChangesAction = {}));
|
|
24
16
|
/**
|
|
25
17
|
* Gets the value of a meta field, action payload, of a defined track-changes action.
|
|
26
18
|
* @param tr
|
|
@@ -38,7 +30,14 @@ function getAction(tr, action) {
|
|
|
38
30
|
*/
|
|
39
31
|
function setAction(tr, action, payload) {
|
|
40
32
|
return tr.setMeta(action, payload);
|
|
41
|
-
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Skip tracking for a transaction, use this with caution to avoid race-conditions or just to otherwise
|
|
36
|
+
* omitting applying of track attributes or marks.
|
|
37
|
+
* @param tr
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
const skipTracking = (tr) => setAction(tr, TrackChangesAction.skipTrack, true);
|
|
42
41
|
|
|
43
42
|
/******************************************************************************
|
|
44
43
|
Copyright (c) Microsoft Corporation.
|
|
@@ -83,7 +82,7 @@ function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
|
83
82
|
* See the License for the specific language governing permissions and
|
|
84
83
|
* limitations under the License.
|
|
85
84
|
*/
|
|
86
|
-
|
|
85
|
+
var CHANGE_OPERATION;
|
|
87
86
|
(function (CHANGE_OPERATION) {
|
|
88
87
|
CHANGE_OPERATION["insert"] = "insert";
|
|
89
88
|
CHANGE_OPERATION["delete"] = "delete";
|
|
@@ -92,13 +91,13 @@ exports.CHANGE_OPERATION = void 0;
|
|
|
92
91
|
CHANGE_OPERATION["unwrap_from_node"] = "unwrap_from_node";
|
|
93
92
|
CHANGE_OPERATION["add_mark"] = "add_mark";
|
|
94
93
|
CHANGE_OPERATION["remove_mark"] = "remove_mark";
|
|
95
|
-
})(
|
|
96
|
-
|
|
94
|
+
})(CHANGE_OPERATION || (CHANGE_OPERATION = {}));
|
|
95
|
+
var CHANGE_STATUS;
|
|
97
96
|
(function (CHANGE_STATUS) {
|
|
98
97
|
CHANGE_STATUS["accepted"] = "accepted";
|
|
99
98
|
CHANGE_STATUS["rejected"] = "rejected";
|
|
100
99
|
CHANGE_STATUS["pending"] = "pending";
|
|
101
|
-
})(
|
|
100
|
+
})(CHANGE_STATUS || (CHANGE_STATUS = {}));
|
|
102
101
|
|
|
103
102
|
/*!
|
|
104
103
|
* © 2021 Atypon Systems LLC
|
|
@@ -115,7 +114,7 @@ exports.CHANGE_STATUS = void 0;
|
|
|
115
114
|
* See the License for the specific language governing permissions and
|
|
116
115
|
* limitations under the License.
|
|
117
116
|
*/
|
|
118
|
-
const logger =
|
|
117
|
+
const logger = debug('track');
|
|
119
118
|
const log = {
|
|
120
119
|
info(str, obj) {
|
|
121
120
|
if (obj) {
|
|
@@ -148,10 +147,10 @@ const log = {
|
|
|
148
147
|
*/
|
|
149
148
|
const enableDebug = (enabled) => {
|
|
150
149
|
if (enabled) {
|
|
151
|
-
|
|
150
|
+
debug.enable('track');
|
|
152
151
|
}
|
|
153
152
|
else {
|
|
154
|
-
|
|
153
|
+
debug.disable();
|
|
155
154
|
}
|
|
156
155
|
};
|
|
157
156
|
|
|
@@ -211,13 +210,13 @@ class ChangeSet {
|
|
|
211
210
|
return rootNodes;
|
|
212
211
|
}
|
|
213
212
|
get pending() {
|
|
214
|
-
return this.changeTree.filter((c) => c.attrs.status ===
|
|
213
|
+
return this.changeTree.filter((c) => c.attrs.status === CHANGE_STATUS.pending);
|
|
215
214
|
}
|
|
216
215
|
get accepted() {
|
|
217
|
-
return this.changeTree.filter((c) => c.attrs.status ===
|
|
216
|
+
return this.changeTree.filter((c) => c.attrs.status === CHANGE_STATUS.accepted);
|
|
218
217
|
}
|
|
219
218
|
get rejected() {
|
|
220
|
-
return this.changeTree.filter((c) => c.attrs.status ===
|
|
219
|
+
return this.changeTree.filter((c) => c.attrs.status === CHANGE_STATUS.rejected);
|
|
221
220
|
}
|
|
222
221
|
get textChanges() {
|
|
223
222
|
return this.changes.filter((c) => c.type === 'text-change');
|
|
@@ -271,8 +270,8 @@ class ChangeSet {
|
|
|
271
270
|
*/
|
|
272
271
|
static shouldNotDelete(change) {
|
|
273
272
|
const { status, operation } = change.attrs;
|
|
274
|
-
return ((operation ===
|
|
275
|
-
(operation ===
|
|
273
|
+
return ((operation === CHANGE_OPERATION.insert && status === CHANGE_STATUS.accepted) ||
|
|
274
|
+
(operation === CHANGE_OPERATION.delete && status === CHANGE_STATUS.rejected));
|
|
276
275
|
}
|
|
277
276
|
/**
|
|
278
277
|
* Determines whether a change should be deleted when applying it to the document.
|
|
@@ -280,8 +279,8 @@ class ChangeSet {
|
|
|
280
279
|
*/
|
|
281
280
|
static shouldDeleteChange(change) {
|
|
282
281
|
const { status, operation } = change.attrs;
|
|
283
|
-
return ((operation ===
|
|
284
|
-
(operation ===
|
|
282
|
+
return ((operation === CHANGE_OPERATION.insert && status === CHANGE_STATUS.rejected) ||
|
|
283
|
+
(operation === CHANGE_OPERATION.delete && status === CHANGE_STATUS.accepted));
|
|
285
284
|
}
|
|
286
285
|
/**
|
|
287
286
|
* Checks whether change attributes contain all TrackedAttrs keys with non-undefined values
|
|
@@ -291,10 +290,22 @@ class ChangeSet {
|
|
|
291
290
|
if ('attrs' in attrs) {
|
|
292
291
|
log.warn('passed "attrs" as property to isValidTrackedAttrs(attrs)', attrs);
|
|
293
292
|
}
|
|
294
|
-
const trackedKeys = [
|
|
295
|
-
|
|
293
|
+
const trackedKeys = [
|
|
294
|
+
'id',
|
|
295
|
+
'authorID',
|
|
296
|
+
'operation',
|
|
297
|
+
'status',
|
|
298
|
+
'createdAt',
|
|
299
|
+
'updatedAt',
|
|
300
|
+
];
|
|
301
|
+
// reviewedByID is set optional since either ProseMirror or Yjs doesn't like persisting null values inside attributes objects
|
|
302
|
+
// So it can be either omitted completely or at least null or string
|
|
303
|
+
const optionalKeys = ['reviewedByID'];
|
|
304
|
+
const entries = Object.entries(attrs).filter(([key, val]) => trackedKeys.includes(key));
|
|
305
|
+
const optionalEntries = Object.entries(attrs).filter(([key, val]) => optionalKeys.includes(key));
|
|
296
306
|
return (entries.length === trackedKeys.length &&
|
|
297
307
|
entries.every(([key, val]) => trackedKeys.includes(key) && val !== undefined) &&
|
|
308
|
+
optionalEntries.every(([key, val]) => optionalKeys.includes(key) && val !== undefined) &&
|
|
298
309
|
(attrs.id || '').length > 0 // Changes created with undefined id have '' as placeholder
|
|
299
310
|
);
|
|
300
311
|
}
|
|
@@ -335,14 +346,17 @@ function deleteNode(node, pos, tr) {
|
|
|
335
346
|
var _a;
|
|
336
347
|
const startPos = tr.doc.resolve(pos + 1);
|
|
337
348
|
const range = startPos.blockRange(tr.doc.resolve(startPos.pos - 2 + node.nodeSize));
|
|
338
|
-
const targetDepth = range
|
|
339
|
-
|
|
349
|
+
const targetDepth = range && liftTarget(range);
|
|
350
|
+
// Check with typeof since with prosemirror-transform pre 1.6.0 targetDepth is undefined
|
|
351
|
+
if (range && typeof targetDepth === 'number') {
|
|
340
352
|
return tr.lift(range, targetDepth);
|
|
341
353
|
}
|
|
342
354
|
const resPos = tr.doc.resolve(pos);
|
|
343
|
-
|
|
355
|
+
// Block nodes can be deleted by just removing their start token which should then merge the text
|
|
356
|
+
// content to above node's content (if there is one)
|
|
357
|
+
const canMergeToNodeAbove = (resPos.parent !== tr.doc || resPos.nodeBefore) && node.isBlock && ((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.isText);
|
|
344
358
|
if (canMergeToNodeAbove) {
|
|
345
|
-
return tr.replaceWith(pos - 1, pos + 1,
|
|
359
|
+
return tr.replaceWith(pos - 1, pos + 1, Fragment.empty);
|
|
346
360
|
}
|
|
347
361
|
else {
|
|
348
362
|
// NOTE: there's an edge case where moving content is not possible but because the immediate
|
|
@@ -375,17 +389,18 @@ function deleteNode(node, pos, tr) {
|
|
|
375
389
|
*/
|
|
376
390
|
function mergeNode(node, pos, tr) {
|
|
377
391
|
var _a;
|
|
378
|
-
if (
|
|
392
|
+
if (canJoin(tr.doc, pos)) {
|
|
379
393
|
return tr.join(pos);
|
|
380
394
|
}
|
|
381
|
-
else if (
|
|
395
|
+
else if (canJoin(tr.doc, pos + node.nodeSize)) {
|
|
396
|
+
// TODO should copy the attributes from the merged node below
|
|
382
397
|
return tr.join(pos + node.nodeSize);
|
|
383
398
|
}
|
|
384
|
-
// TODO is this the same thing as join?
|
|
399
|
+
// TODO is this the same thing as join to above?
|
|
385
400
|
const resPos = tr.doc.resolve(pos);
|
|
386
401
|
const canMergeToNodeAbove = (resPos.parent !== tr.doc || resPos.nodeBefore) && ((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.isText);
|
|
387
402
|
if (canMergeToNodeAbove) {
|
|
388
|
-
return tr.replaceWith(pos - 1, pos + 1,
|
|
403
|
+
return tr.replaceWith(pos - 1, pos + 1, Fragment.empty);
|
|
389
404
|
}
|
|
390
405
|
return undefined;
|
|
391
406
|
}
|
|
@@ -429,8 +444,8 @@ function getInlineNodeTrackedMarkData(node, schema) {
|
|
|
429
444
|
node.marks.forEach((mark) => {
|
|
430
445
|
if (mark.type === schema.marks.tracked_insert || mark.type === schema.marks.tracked_delete) {
|
|
431
446
|
const operation = mark.type === schema.marks.tracked_insert
|
|
432
|
-
?
|
|
433
|
-
:
|
|
447
|
+
? CHANGE_OPERATION.insert
|
|
448
|
+
: CHANGE_OPERATION.delete;
|
|
434
449
|
marksTrackedData.push({ ...mark.attrs.dataTracked, operation });
|
|
435
450
|
}
|
|
436
451
|
});
|
|
@@ -460,7 +475,7 @@ function shouldMergeTrackedAttributes(left, right) {
|
|
|
460
475
|
}
|
|
461
476
|
return (left.status === right.status &&
|
|
462
477
|
left.operation === right.operation &&
|
|
463
|
-
left.
|
|
478
|
+
left.authorID === right.authorID);
|
|
464
479
|
}
|
|
465
480
|
function getMergeableMarkTrackedAttrs(node, attrs, schema) {
|
|
466
481
|
const nodeAttrs = getInlineNodeTrackedMarkData(node, schema);
|
|
@@ -504,16 +519,16 @@ function updateChangeChildrenAttributes(changes, tr, mapping) {
|
|
|
504
519
|
* @param changes
|
|
505
520
|
* @param deleteMap
|
|
506
521
|
*/
|
|
507
|
-
function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new
|
|
522
|
+
function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new Mapping()) {
|
|
508
523
|
changes.forEach((change) => {
|
|
509
|
-
if (change.attrs.status ===
|
|
524
|
+
if (change.attrs.status === CHANGE_STATUS.pending) {
|
|
510
525
|
return;
|
|
511
526
|
}
|
|
512
527
|
// Map change.from and skip those which dont need to be applied
|
|
513
528
|
// or were already deleted by an applied block delete
|
|
514
529
|
const { pos: from, deleted } = deleteMap.mapResult(change.from), node = tr.doc.nodeAt(from), noChangeNeeded = deleted || ChangeSet.shouldNotDelete(change);
|
|
515
530
|
if (!node) {
|
|
516
|
-
log.warn('no node found to update for change', change);
|
|
531
|
+
!deleted && log.warn('no node found to update for change', change);
|
|
517
532
|
return;
|
|
518
533
|
}
|
|
519
534
|
if (ChangeSet.isTextChange(change) && noChangeNeeded) {
|
|
@@ -580,6 +595,7 @@ function findChanges(state) {
|
|
|
580
595
|
type: 'text-change',
|
|
581
596
|
from: pos,
|
|
582
597
|
to: pos + node.nodeSize,
|
|
598
|
+
text: node.text,
|
|
583
599
|
attrs,
|
|
584
600
|
},
|
|
585
601
|
node,
|
|
@@ -624,13 +640,15 @@ function fixInconsistentChanges(changeSet, trackUserID, newTr, schema) {
|
|
|
624
640
|
const iteratedIds = new Set();
|
|
625
641
|
let changed = false;
|
|
626
642
|
changeSet.invalidChanges.forEach((c) => {
|
|
627
|
-
const { id,
|
|
643
|
+
const { id, authorID, operation, reviewedByID, status, createdAt, updatedAt } = c.attrs;
|
|
628
644
|
const newAttrs = {
|
|
629
645
|
...((!id || iteratedIds.has(id) || id.length === 0) && { id: uuidv4() }),
|
|
630
|
-
...(!
|
|
631
|
-
...(!operation && { operation:
|
|
632
|
-
...(!
|
|
646
|
+
...(!authorID && { authorID: trackUserID }),
|
|
647
|
+
...(!operation && { operation: CHANGE_OPERATION.insert }),
|
|
648
|
+
...(!reviewedByID && { reviewedByID: null }),
|
|
649
|
+
...(!status && { status: CHANGE_STATUS.pending }),
|
|
633
650
|
...(!createdAt && { createdAt: Date.now() }),
|
|
651
|
+
...(!updatedAt && { updatedAt: Date.now() }),
|
|
634
652
|
};
|
|
635
653
|
if (Object.keys(newAttrs).length > 0) {
|
|
636
654
|
updateChangeAttrs(newTr, c, { ...c.attrs, ...newAttrs }, schema);
|
|
@@ -658,7 +676,7 @@ function fixInconsistentChanges(changeSet, trackUserID, newTr, schema) {
|
|
|
658
676
|
*/
|
|
659
677
|
function markInlineNodeChange(node, newTrackAttrs, schema) {
|
|
660
678
|
const filtered = node.marks.filter((m) => m.type !== schema.marks.tracked_insert && m.type !== schema.marks.tracked_delete);
|
|
661
|
-
const mark = newTrackAttrs.operation ===
|
|
679
|
+
const mark = newTrackAttrs.operation === CHANGE_OPERATION.insert
|
|
662
680
|
? schema.marks.tracked_insert
|
|
663
681
|
: schema.marks.tracked_delete;
|
|
664
682
|
const createdMark = mark.create({
|
|
@@ -678,7 +696,7 @@ function recurseNodeContent(node, newTrackAttrs, schema) {
|
|
|
678
696
|
return node.type.create({
|
|
679
697
|
...node.attrs,
|
|
680
698
|
dataTracked: addTrackIdIfDoesntExist(newTrackAttrs),
|
|
681
|
-
},
|
|
699
|
+
}, Fragment.fromArray(updatedChildren), node.marks);
|
|
682
700
|
}
|
|
683
701
|
else {
|
|
684
702
|
log.error(`unhandled node type: "${node.type.name}"`, node);
|
|
@@ -691,7 +709,7 @@ function setFragmentAsInserted(inserted, insertAttrs, schema) {
|
|
|
691
709
|
inserted.forEach((n) => {
|
|
692
710
|
updatedInserted.push(recurseNodeContent(n, insertAttrs, schema));
|
|
693
711
|
});
|
|
694
|
-
return updatedInserted.length === 0 ? inserted :
|
|
712
|
+
return updatedInserted.length === 0 ? inserted : Fragment.fromArray(updatedInserted);
|
|
695
713
|
}
|
|
696
714
|
|
|
697
715
|
/*!
|
|
@@ -712,13 +730,13 @@ function setFragmentAsInserted(inserted, insertAttrs, schema) {
|
|
|
712
730
|
function createNewInsertAttrs(attrs) {
|
|
713
731
|
return {
|
|
714
732
|
...attrs,
|
|
715
|
-
operation:
|
|
733
|
+
operation: CHANGE_OPERATION.insert,
|
|
716
734
|
};
|
|
717
735
|
}
|
|
718
736
|
function createNewDeleteAttrs(attrs) {
|
|
719
737
|
return {
|
|
720
738
|
...attrs,
|
|
721
|
-
operation:
|
|
739
|
+
operation: CHANGE_OPERATION.delete,
|
|
722
740
|
};
|
|
723
741
|
}
|
|
724
742
|
|
|
@@ -761,7 +779,7 @@ function getMergedNode(node, currentDepth, depth, first) {
|
|
|
761
779
|
};
|
|
762
780
|
}
|
|
763
781
|
const result = [];
|
|
764
|
-
let merged =
|
|
782
|
+
let merged = Fragment.empty;
|
|
765
783
|
node.content.forEach((n, _, i) => {
|
|
766
784
|
if ((first && i === 0) || (!first && i === node.childCount - 1)) {
|
|
767
785
|
const { mergedNodeContent, unmergedContent } = getMergedNode(n, currentDepth + 1, depth, first);
|
|
@@ -776,7 +794,7 @@ function getMergedNode(node, currentDepth, depth, first) {
|
|
|
776
794
|
});
|
|
777
795
|
return {
|
|
778
796
|
mergedNodeContent: merged,
|
|
779
|
-
unmergedContent: result.length > 0 ?
|
|
797
|
+
unmergedContent: result.length > 0 ? Fragment.fromArray(result) : undefined,
|
|
780
798
|
};
|
|
781
799
|
}
|
|
782
800
|
/**
|
|
@@ -839,7 +857,7 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
|
|
|
839
857
|
// Math.max(pos, from) is for picking always the start of the node,
|
|
840
858
|
// not the start of the change (which might span multiple nodes).
|
|
841
859
|
// Pos can be less than from as nodesBetween iterates through all nodes starting from the top block node
|
|
842
|
-
newTr.replaceWith(start, end,
|
|
860
|
+
newTr.replaceWith(start, end, Fragment.empty);
|
|
843
861
|
return start;
|
|
844
862
|
}
|
|
845
863
|
else {
|
|
@@ -849,10 +867,12 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
|
|
|
849
867
|
const rightMarks = getMergeableMarkTrackedAttrs(rightNode, deleteAttrs, schema);
|
|
850
868
|
const fromStartOfMark = start - (leftNode && leftMarks ? leftNode.nodeSize : 0);
|
|
851
869
|
const toEndOfMark = end + (rightNode && rightMarks ? rightNode.nodeSize : 0);
|
|
870
|
+
const createdAt = Math.min((leftMarks === null || leftMarks === void 0 ? void 0 : leftMarks.createdAt) || Number.MAX_VALUE, (rightMarks === null || rightMarks === void 0 ? void 0 : rightMarks.createdAt) || Number.MAX_VALUE, deleteAttrs.createdAt);
|
|
852
871
|
const dataTracked = addTrackIdIfDoesntExist({
|
|
853
872
|
...leftMarks,
|
|
854
873
|
...rightMarks,
|
|
855
874
|
...deleteAttrs,
|
|
875
|
+
createdAt,
|
|
856
876
|
});
|
|
857
877
|
newTr.addMark(fromStartOfMark, toEndOfMark, schema.marks.tracked_delete.create({
|
|
858
878
|
dataTracked,
|
|
@@ -869,7 +889,8 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
|
|
|
869
889
|
*/
|
|
870
890
|
function deleteOrSetNodeDeleted(node, pos, newTr, deleteAttrs) {
|
|
871
891
|
const dataTracked = node.attrs.dataTracked;
|
|
872
|
-
const wasInsertedBySameUser = (dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.operation) ===
|
|
892
|
+
const wasInsertedBySameUser = (dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.operation) === CHANGE_OPERATION.insert &&
|
|
893
|
+
dataTracked.authorID === deleteAttrs.authorID;
|
|
873
894
|
if (wasInsertedBySameUser) {
|
|
874
895
|
deleteNode(node, pos, newTr);
|
|
875
896
|
}
|
|
@@ -907,7 +928,7 @@ function deleteOrSetNodeDeleted(node, pos, newTr, deleteAttrs) {
|
|
|
907
928
|
* @returns mapping adjusted by the applied operations & modified insert slice
|
|
908
929
|
*/
|
|
909
930
|
function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackAttrs, insertSlice) {
|
|
910
|
-
const deleteMap = new
|
|
931
|
+
const deleteMap = new Mapping();
|
|
911
932
|
const mergedInsertPos = undefined;
|
|
912
933
|
// No deletion applied, return default values
|
|
913
934
|
if (from === to) {
|
|
@@ -925,10 +946,22 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
|
|
|
925
946
|
const { pos: offsetPos, deleted: nodeWasDeleted } = deleteMap.mapResult(pos, 1);
|
|
926
947
|
const offsetFrom = deleteMap.map(from, -1);
|
|
927
948
|
const offsetTo = deleteMap.map(to, 1);
|
|
928
|
-
const wasWithinGap = gap && offsetPos >= deleteMap.map(gap.start, -1);
|
|
929
949
|
const nodeEnd = offsetPos + node.nodeSize;
|
|
950
|
+
// So this insane boolean checks for ReplaceAroundStep gaps and whether the node should be skipped
|
|
951
|
+
// since the content inside gap should stay unchanged.
|
|
952
|
+
// All other nodes except text nodes consist of one start and end token (or just a single token for atoms).
|
|
953
|
+
// For them we can just check whether the start token is within the gap eg pos is 10 when gap (8, 18) to
|
|
954
|
+
// determine whether it should be skipped.
|
|
955
|
+
// For text nodes though, since they are continous, they might only partially be enclosed in the gap
|
|
956
|
+
// eg. pos 10 when gap is (8, 18) BUT if their nodeEnd goes past the gap's end eg nodeEnd 20 they actually
|
|
957
|
+
// are altered and should not be skipped.
|
|
958
|
+
// @TODO ATM 20.7.2022 there doesn't seem to be tests that capture this.
|
|
959
|
+
const wasWithinGap = gap &&
|
|
960
|
+
((!node.isText && offsetPos >= deleteMap.map(gap.start, -1)) ||
|
|
961
|
+
(node.isText &&
|
|
962
|
+
offsetPos <= deleteMap.map(gap.start, -1) &&
|
|
963
|
+
nodeEnd >= deleteMap.map(gap.end, -1)));
|
|
930
964
|
let step = newTr.steps[newTr.steps.length - 1];
|
|
931
|
-
// debugger
|
|
932
965
|
// nodeEnd > offsetFrom -> delete touches this node
|
|
933
966
|
// eg (del 6 10) <p 5>|<t 6>cdf</t 9></p 10>| -> <p> nodeEnd 10 > from 6
|
|
934
967
|
//
|
|
@@ -937,6 +970,7 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
|
|
|
937
970
|
// But from what I remember what it safeguards against is, when you've already deleted a node
|
|
938
971
|
// say an inserted blockquote that had all its children deleted, nodesBetween still iterates over those
|
|
939
972
|
// nodes and therefore we have to make this check to ensure they still exist in the doc.
|
|
973
|
+
//
|
|
940
974
|
if (nodeEnd > offsetFrom && !nodeWasDeleted && !wasWithinGap) {
|
|
941
975
|
// |<p>asdf</p>| -> node deleted completely
|
|
942
976
|
const nodeCompletelyDeleted = offsetPos >= offsetFrom && nodeEnd <= offsetTo;
|
|
@@ -1029,7 +1063,7 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
|
|
|
1029
1063
|
deleteMap,
|
|
1030
1064
|
mergedInsertPos,
|
|
1031
1065
|
newSliceContent: updatedSliceNodes
|
|
1032
|
-
?
|
|
1066
|
+
? Fragment.fromArray(updatedSliceNodes)
|
|
1033
1067
|
: insertSlice.content,
|
|
1034
1068
|
};
|
|
1035
1069
|
}
|
|
@@ -1053,18 +1087,21 @@ function mergeTrackedMarks(pos, doc, newTr, schema) {
|
|
|
1053
1087
|
if (!nodeAfter || !nodeBefore || !leftMark || !rightMark || leftMark.type !== rightMark.type) {
|
|
1054
1088
|
return;
|
|
1055
1089
|
}
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
if (!shouldMergeTrackedAttributes(
|
|
1090
|
+
const leftDataTracked = leftMark.attrs.dataTracked;
|
|
1091
|
+
const rightDataTracked = rightMark.attrs.dataTracked;
|
|
1092
|
+
if (!shouldMergeTrackedAttributes(leftDataTracked, rightDataTracked)) {
|
|
1059
1093
|
return;
|
|
1060
1094
|
}
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1095
|
+
const isLeftOlder = (leftDataTracked.createdAt || Number.MAX_VALUE) <
|
|
1096
|
+
(rightDataTracked.createdAt || Number.MAX_VALUE);
|
|
1097
|
+
const ancestorAttrs = isLeftOlder ? leftDataTracked : rightDataTracked;
|
|
1098
|
+
const dataTracked = {
|
|
1099
|
+
...ancestorAttrs,
|
|
1100
|
+
updatedAt: Date.now(),
|
|
1064
1101
|
};
|
|
1065
1102
|
const fromStartOfMark = pos - nodeBefore.nodeSize;
|
|
1066
1103
|
const toEndOfMark = pos + nodeAfter.nodeSize;
|
|
1067
|
-
newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create(
|
|
1104
|
+
newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create({ ...leftMark.attrs, dataTracked }));
|
|
1068
1105
|
}
|
|
1069
1106
|
|
|
1070
1107
|
/*!
|
|
@@ -1128,13 +1165,13 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
|
|
|
1128
1165
|
// the sides should be equal. TODO can they be other than 0?
|
|
1129
1166
|
const openStart = slice.openStart !== slice.openEnd || newSliceContent.size === 0 ? 0 : slice.openStart;
|
|
1130
1167
|
const openEnd = slice.openStart !== slice.openEnd || newSliceContent.size === 0 ? 0 : slice.openEnd;
|
|
1131
|
-
let insertedSlice = new
|
|
1168
|
+
let insertedSlice = new Slice(setFragmentAsInserted(newSliceContent, createNewInsertAttrs(attrs), oldState.schema), openStart, openEnd);
|
|
1132
1169
|
if (gap.size > 0) {
|
|
1133
1170
|
log.info('insertedSlice before inserted gap', insertedSlice);
|
|
1134
1171
|
insertedSlice = insertedSlice.insertAt(insertedSlice.size === 0 ? 0 : insert, gap.content);
|
|
1135
1172
|
log.info('insertedSlice after inserted gap', insertedSlice);
|
|
1136
1173
|
}
|
|
1137
|
-
const newStep = new
|
|
1174
|
+
const newStep = new ReplaceStep(deleteMap.map(gapFrom), deleteMap.map(gapTo), insertedSlice, false);
|
|
1138
1175
|
const stepResult = newTr.maybeStep(newStep);
|
|
1139
1176
|
if (stepResult.failed) {
|
|
1140
1177
|
log.error(`insert ReplaceStep failed: "${stepResult.failed}"`, newStep);
|
|
@@ -1179,10 +1216,11 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1179
1216
|
log.error(`invert ReplaceStep failed: "${stepResult.failed}"`, newStep);
|
|
1180
1217
|
return;
|
|
1181
1218
|
}
|
|
1219
|
+
log.info('TR: steps before applying delete', [...newTr.steps]);
|
|
1182
1220
|
// First apply the deleted range and update the insert slice to not include content that was deleted,
|
|
1183
1221
|
// eg partial nodes in an open-ended slice
|
|
1184
1222
|
const { deleteMap, mergedInsertPos, newSliceContent } = deleteAndMergeSplitNodes(fromA, toA, undefined, oldState.doc, newTr, oldState.schema, attrs, slice);
|
|
1185
|
-
log.info('TR:
|
|
1223
|
+
log.info('TR: steps after applying delete', [...newTr.steps]);
|
|
1186
1224
|
const adjustedInsertPos = mergedInsertPos !== null && mergedInsertPos !== void 0 ? mergedInsertPos : deleteMap.map(toA);
|
|
1187
1225
|
if (newSliceContent.size > 0) {
|
|
1188
1226
|
log.info('newSliceContent', newSliceContent);
|
|
@@ -1190,8 +1228,8 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1190
1228
|
// the sides should be equal. TODO can they be other than 0?
|
|
1191
1229
|
const openStart = slice.openStart !== slice.openEnd ? 0 : slice.openStart;
|
|
1192
1230
|
const openEnd = slice.openStart !== slice.openEnd ? 0 : slice.openEnd;
|
|
1193
|
-
const insertedSlice = new
|
|
1194
|
-
const newStep = new
|
|
1231
|
+
const insertedSlice = new Slice(setFragmentAsInserted(newSliceContent, createNewInsertAttrs(attrs), oldState.schema), openStart, openEnd);
|
|
1232
|
+
const newStep = new ReplaceStep(adjustedInsertPos, adjustedInsertPos, insertedSlice);
|
|
1195
1233
|
const stepResult = newTr.maybeStep(newStep);
|
|
1196
1234
|
if (stepResult.failed) {
|
|
1197
1235
|
log.error(`insert ReplaceStep failed: "${stepResult.failed}"`, newStep);
|
|
@@ -1217,11 +1255,9 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1217
1255
|
* This skips the direct dependency to prosemirror-state where multiple versions might cause conflicts
|
|
1218
1256
|
* as the created instances might belong to different prosemirror-state import than one used in the editor.
|
|
1219
1257
|
* @param sel
|
|
1220
|
-
* @param doc
|
|
1221
|
-
* @param from
|
|
1222
1258
|
* @returns
|
|
1223
1259
|
*/
|
|
1224
|
-
const
|
|
1260
|
+
const getSelectionStaticConstructor = (sel) => Object.getPrototypeOf(sel).constructor;
|
|
1225
1261
|
/**
|
|
1226
1262
|
* Inverts transactions to wrap their contents/operations with track data instead
|
|
1227
1263
|
*
|
|
@@ -1234,15 +1270,16 @@ const getSelectionStaticCreate = (sel, doc, from) => Object.getPrototypeOf(sel).
|
|
|
1234
1270
|
* @param tr Original transaction
|
|
1235
1271
|
* @param oldState State before transaction
|
|
1236
1272
|
* @param newTr Transaction created from the new editor state
|
|
1237
|
-
* @param
|
|
1273
|
+
* @param authorID User id
|
|
1238
1274
|
* @returns newTr that inverts the initial tr and applies track attributes/marks
|
|
1239
1275
|
*/
|
|
1240
|
-
function trackTransaction(tr, oldState, newTr,
|
|
1241
|
-
var _a;
|
|
1276
|
+
function trackTransaction(tr, oldState, newTr, authorID) {
|
|
1242
1277
|
const emptyAttrs = {
|
|
1243
|
-
|
|
1278
|
+
authorID,
|
|
1279
|
+
reviewedByID: null,
|
|
1244
1280
|
createdAt: tr.time,
|
|
1245
|
-
|
|
1281
|
+
updatedAt: tr.time,
|
|
1282
|
+
status: CHANGE_STATUS.pending,
|
|
1246
1283
|
};
|
|
1247
1284
|
// Must use constructor.name instead of instanceof as aliasing prosemirror-state is a lot more
|
|
1248
1285
|
// difficult than prosemirror-transform
|
|
@@ -1257,45 +1294,56 @@ function trackTransaction(tr, oldState, newTr, userID) {
|
|
|
1257
1294
|
'This is probably an error with the library, please report back to maintainers with a reproduction if possible', newTr);
|
|
1258
1295
|
return;
|
|
1259
1296
|
}
|
|
1260
|
-
else if (!(step instanceof
|
|
1297
|
+
else if (!(step instanceof ReplaceStep) && step.constructor.name === 'ReplaceStep') {
|
|
1261
1298
|
console.error('@manuscripts/track-changes-plugin: Multiple prosemirror-transform packages imported, alias/dedupe them ' +
|
|
1262
1299
|
'or instanceof checks fail as well as creating new steps');
|
|
1263
1300
|
return;
|
|
1264
1301
|
}
|
|
1265
|
-
else if (step instanceof
|
|
1302
|
+
else if (step instanceof ReplaceStep) {
|
|
1266
1303
|
const selectionPos = trackReplaceStep(step, oldState, newTr, emptyAttrs);
|
|
1267
1304
|
if (!wasNodeSelection) {
|
|
1268
|
-
|
|
1305
|
+
const sel = getSelectionStaticConstructor(tr.selection);
|
|
1306
|
+
// Use Selection.near to fix selections that point to a block node instead of inline content
|
|
1307
|
+
// eg when inserting a complete new paragraph. -1 finds the first valid position moving backwards
|
|
1308
|
+
// inside the content
|
|
1309
|
+
const near = sel.near(newTr.doc.resolve(selectionPos), -1);
|
|
1310
|
+
newTr.setSelection(near);
|
|
1269
1311
|
}
|
|
1270
1312
|
}
|
|
1271
|
-
else if (step instanceof
|
|
1313
|
+
else if (step instanceof ReplaceAroundStep) {
|
|
1272
1314
|
trackReplaceAroundStep(step, oldState, newTr, emptyAttrs);
|
|
1273
1315
|
// } else if (step instanceof AddMarkStep) {
|
|
1274
1316
|
// } else if (step instanceof RemoveMarkStep) {
|
|
1275
1317
|
}
|
|
1318
|
+
// TODO: here we could check whether adjacent inserts & deletes cancel each other out.
|
|
1319
|
+
// However, this should not be done by diffing and only matching node or char by char instead since
|
|
1320
|
+
// it's A easier and B more intuitive to user.
|
|
1276
1321
|
// The old meta keys are not copied to the new transaction since this will cause race-conditions
|
|
1277
|
-
// when a single meta-field is
|
|
1278
|
-
// inputType
|
|
1279
|
-
//
|
|
1322
|
+
// when a single meta-field is expected to having been processed / removed. Generic input meta keys,
|
|
1323
|
+
// inputType and uiEvent, are re-added since some plugins might depend on them and process the transaction
|
|
1324
|
+
// after track-changes plugin.
|
|
1325
|
+
tr.getMeta('inputType') && newTr.setMeta('inputType', tr.getMeta('inputType'));
|
|
1326
|
+
tr.getMeta('uiEvent') && newTr.setMeta('uiEvent', tr.getMeta('uiEvent'));
|
|
1280
1327
|
});
|
|
1281
1328
|
// This is kinda hacky solution at the moment to maintain NodeSelections over transactions
|
|
1282
|
-
// These are required by at least cross-references
|
|
1329
|
+
// These are required by at least cross-references and links to activate their selector pop-ups
|
|
1283
1330
|
if (wasNodeSelection) {
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
const
|
|
1287
|
-
|
|
1331
|
+
// And -1 here is necessary to keep the selection pointing at the start of the node
|
|
1332
|
+
// (or something, breaks with cross-references otherwise)
|
|
1333
|
+
const mappedPos = newTr.mapping.map(tr.selection.from, -1);
|
|
1334
|
+
const sel = getSelectionStaticConstructor(tr.selection);
|
|
1335
|
+
newTr.setSelection(sel.create(newTr.doc, mappedPos));
|
|
1288
1336
|
}
|
|
1289
1337
|
log.info('NEW transaction', newTr);
|
|
1290
1338
|
return newTr;
|
|
1291
1339
|
}
|
|
1292
1340
|
|
|
1293
|
-
|
|
1341
|
+
var TrackChangesStatus;
|
|
1294
1342
|
(function (TrackChangesStatus) {
|
|
1295
1343
|
TrackChangesStatus["enabled"] = "enabled";
|
|
1296
1344
|
TrackChangesStatus["viewSnapshots"] = "view-snapshots";
|
|
1297
1345
|
TrackChangesStatus["disabled"] = "disabled";
|
|
1298
|
-
})(
|
|
1346
|
+
})(TrackChangesStatus || (TrackChangesStatus = {}));
|
|
1299
1347
|
|
|
1300
1348
|
/*!
|
|
1301
1349
|
* © 2021 Atypon Systems LLC
|
|
@@ -1312,12 +1360,7 @@ exports.TrackChangesStatus = void 0;
|
|
|
1312
1360
|
* See the License for the specific language governing permissions and
|
|
1313
1361
|
* limitations under the License.
|
|
1314
1362
|
*/
|
|
1315
|
-
const trackChangesPluginKey = new
|
|
1316
|
-
// TODO remove
|
|
1317
|
-
const infiniteLoopCounter = {
|
|
1318
|
-
start: 0,
|
|
1319
|
-
iters: 0,
|
|
1320
|
-
};
|
|
1363
|
+
const trackChangesPluginKey = new PluginKey('track-changes');
|
|
1321
1364
|
/**
|
|
1322
1365
|
* The ProseMirror plugin needed to enable track-changes.
|
|
1323
1366
|
*
|
|
@@ -1330,24 +1373,25 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1330
1373
|
if (debug) {
|
|
1331
1374
|
enableDebug(true);
|
|
1332
1375
|
}
|
|
1333
|
-
return new
|
|
1376
|
+
return new Plugin({
|
|
1334
1377
|
key: trackChangesPluginKey,
|
|
1335
1378
|
props: {
|
|
1336
1379
|
editable(state) {
|
|
1337
|
-
|
|
1380
|
+
var _a;
|
|
1381
|
+
return ((_a = trackChangesPluginKey.getState(state)) === null || _a === void 0 ? void 0 : _a.status) !== TrackChangesStatus.viewSnapshots;
|
|
1338
1382
|
},
|
|
1339
1383
|
},
|
|
1340
1384
|
state: {
|
|
1341
1385
|
init(_config, state) {
|
|
1342
1386
|
return {
|
|
1343
|
-
status:
|
|
1387
|
+
status: TrackChangesStatus.enabled,
|
|
1344
1388
|
userID,
|
|
1345
1389
|
changeSet: findChanges(state),
|
|
1346
1390
|
};
|
|
1347
1391
|
},
|
|
1348
1392
|
apply(tr, pluginState, _oldState, newState) {
|
|
1349
|
-
const setUserID = getAction(tr,
|
|
1350
|
-
const setStatus = getAction(tr,
|
|
1393
|
+
const setUserID = getAction(tr, TrackChangesAction.setUserID);
|
|
1394
|
+
const setStatus = getAction(tr, TrackChangesAction.setPluginStatus);
|
|
1351
1395
|
if (setUserID) {
|
|
1352
1396
|
return { ...pluginState, userID: setUserID };
|
|
1353
1397
|
}
|
|
@@ -1358,12 +1402,12 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1358
1402
|
changeSet: findChanges(newState),
|
|
1359
1403
|
};
|
|
1360
1404
|
}
|
|
1361
|
-
else if (pluginState.status ===
|
|
1405
|
+
else if (pluginState.status === TrackChangesStatus.disabled) {
|
|
1362
1406
|
return { ...pluginState, changeSet: new ChangeSet() };
|
|
1363
1407
|
}
|
|
1364
1408
|
let { changeSet, ...rest } = pluginState;
|
|
1365
|
-
const updatedChangeIds = getAction(tr,
|
|
1366
|
-
if (updatedChangeIds || getAction(tr,
|
|
1409
|
+
const updatedChangeIds = getAction(tr, TrackChangesAction.updateChanges);
|
|
1410
|
+
if (updatedChangeIds || getAction(tr, TrackChangesAction.refreshChanges)) {
|
|
1367
1411
|
changeSet = findChanges(newState);
|
|
1368
1412
|
}
|
|
1369
1413
|
return {
|
|
@@ -1382,47 +1426,37 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1382
1426
|
appendTransaction(trs, oldState, newState) {
|
|
1383
1427
|
const pluginState = trackChangesPluginKey.getState(newState);
|
|
1384
1428
|
if (!pluginState ||
|
|
1385
|
-
pluginState.status ===
|
|
1429
|
+
pluginState.status === TrackChangesStatus.disabled ||
|
|
1386
1430
|
!(editorView === null || editorView === void 0 ? void 0 : editorView.editable)) {
|
|
1387
1431
|
return null;
|
|
1388
1432
|
}
|
|
1389
|
-
if (infiniteLoopCounter.start < Date.now() - 10000) {
|
|
1390
|
-
infiniteLoopCounter.start = Date.now();
|
|
1391
|
-
infiniteLoopCounter.iters = 0;
|
|
1392
|
-
}
|
|
1393
|
-
if (infiniteLoopCounter.iters >= 100) {
|
|
1394
|
-
console.error('Detected probable infinite loop in track changes!');
|
|
1395
|
-
return null;
|
|
1396
|
-
}
|
|
1397
1433
|
const { userID, changeSet } = pluginState;
|
|
1398
1434
|
let createdTr = newState.tr, docChanged = false;
|
|
1399
1435
|
log.info('TRS', trs);
|
|
1400
1436
|
trs.forEach((tr) => {
|
|
1401
1437
|
const wasAppended = tr.getMeta('appendedTransaction');
|
|
1402
1438
|
const skipMetaUsed = skipTrsWithMetas.some((m) => tr.getMeta(m) || (wasAppended === null || wasAppended === void 0 ? void 0 : wasAppended.getMeta(m)));
|
|
1403
|
-
const skipTrackUsed = getAction(tr,
|
|
1404
|
-
(wasAppended && getAction(wasAppended,
|
|
1439
|
+
const skipTrackUsed = getAction(tr, TrackChangesAction.skipTrack) ||
|
|
1440
|
+
(wasAppended && getAction(wasAppended, TrackChangesAction.skipTrack));
|
|
1405
1441
|
if (tr.docChanged && !skipMetaUsed && !skipTrackUsed && !tr.getMeta('history$')) {
|
|
1406
1442
|
createdTr = trackTransaction(tr, oldState, createdTr, userID);
|
|
1407
|
-
createdTr.setMeta('origin', trackChangesPluginKey);
|
|
1408
|
-
infiniteLoopCounter.iters += 1;
|
|
1409
1443
|
}
|
|
1410
1444
|
docChanged = docChanged || tr.docChanged;
|
|
1411
|
-
const setChangeStatuses = getAction(tr,
|
|
1445
|
+
const setChangeStatuses = getAction(tr, TrackChangesAction.setChangeStatuses);
|
|
1412
1446
|
if (setChangeStatuses) {
|
|
1413
1447
|
const { status, ids } = setChangeStatuses;
|
|
1414
1448
|
ids.forEach((changeId) => {
|
|
1415
1449
|
const change = changeSet === null || changeSet === void 0 ? void 0 : changeSet.get(changeId);
|
|
1416
1450
|
if (change) {
|
|
1417
|
-
createdTr = updateChangeAttrs(createdTr, change, { status }, oldState.schema);
|
|
1418
|
-
setAction(createdTr,
|
|
1451
|
+
createdTr = updateChangeAttrs(createdTr, change, { status, reviewedByID: userID }, oldState.schema);
|
|
1452
|
+
setAction(createdTr, TrackChangesAction.updateChanges, [change.id]);
|
|
1419
1453
|
}
|
|
1420
1454
|
});
|
|
1421
1455
|
}
|
|
1422
|
-
else if (getAction(tr,
|
|
1456
|
+
else if (getAction(tr, TrackChangesAction.applyAndRemoveChanges)) {
|
|
1423
1457
|
const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.nodeChanges);
|
|
1424
1458
|
applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.textChanges, mapping);
|
|
1425
|
-
setAction(createdTr,
|
|
1459
|
+
setAction(createdTr, TrackChangesAction.refreshChanges, true);
|
|
1426
1460
|
}
|
|
1427
1461
|
});
|
|
1428
1462
|
const changed = pluginState.changeSet.hasInconsistentData &&
|
|
@@ -1431,7 +1465,8 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1431
1465
|
log.warn('had to fix inconsistent changes in', createdTr);
|
|
1432
1466
|
}
|
|
1433
1467
|
if (docChanged || createdTr.docChanged || changed) {
|
|
1434
|
-
|
|
1468
|
+
createdTr.setMeta('origin', trackChangesPluginKey);
|
|
1469
|
+
return setAction(createdTr, TrackChangesAction.refreshChanges, true);
|
|
1435
1470
|
}
|
|
1436
1471
|
return null;
|
|
1437
1472
|
},
|
|
@@ -1469,11 +1504,11 @@ const setTrackingStatus = (status) => (state, dispatch) => {
|
|
|
1469
1504
|
let newStatus = status;
|
|
1470
1505
|
if (newStatus === undefined) {
|
|
1471
1506
|
newStatus =
|
|
1472
|
-
currentStatus ===
|
|
1473
|
-
?
|
|
1474
|
-
:
|
|
1507
|
+
currentStatus === TrackChangesStatus.enabled
|
|
1508
|
+
? TrackChangesStatus.disabled
|
|
1509
|
+
: TrackChangesStatus.enabled;
|
|
1475
1510
|
}
|
|
1476
|
-
dispatch && dispatch(setAction(state.tr,
|
|
1511
|
+
dispatch && dispatch(setAction(state.tr, TrackChangesAction.setPluginStatus, newStatus));
|
|
1477
1512
|
return true;
|
|
1478
1513
|
}
|
|
1479
1514
|
return false;
|
|
@@ -1485,7 +1520,7 @@ const setTrackingStatus = (status) => (state, dispatch) => {
|
|
|
1485
1520
|
*/
|
|
1486
1521
|
const setChangeStatuses = (status, ids) => (state, dispatch) => {
|
|
1487
1522
|
dispatch &&
|
|
1488
|
-
dispatch(setAction(state.tr,
|
|
1523
|
+
dispatch(setAction(state.tr, TrackChangesAction.setChangeStatuses, {
|
|
1489
1524
|
status,
|
|
1490
1525
|
ids,
|
|
1491
1526
|
}));
|
|
@@ -1496,21 +1531,21 @@ const setChangeStatuses = (status, ids) => (state, dispatch) => {
|
|
|
1496
1531
|
* @param userID
|
|
1497
1532
|
*/
|
|
1498
1533
|
const setUserID = (userID) => (state, dispatch) => {
|
|
1499
|
-
dispatch && dispatch(setAction(state.tr,
|
|
1534
|
+
dispatch && dispatch(setAction(state.tr, TrackChangesAction.setUserID, userID));
|
|
1500
1535
|
return true;
|
|
1501
1536
|
};
|
|
1502
1537
|
/**
|
|
1503
1538
|
* Appends a transaction that applies all 'accepted' and 'rejected' changes to the document.
|
|
1504
1539
|
*/
|
|
1505
1540
|
const applyAndRemoveChanges = () => (state, dispatch) => {
|
|
1506
|
-
dispatch && dispatch(setAction(state.tr,
|
|
1541
|
+
dispatch && dispatch(setAction(state.tr, TrackChangesAction.applyAndRemoveChanges, true));
|
|
1507
1542
|
return true;
|
|
1508
1543
|
};
|
|
1509
1544
|
/**
|
|
1510
1545
|
* Runs `findChanges` to iterate over the document to collect changes into a new ChangeSet.
|
|
1511
1546
|
*/
|
|
1512
1547
|
const refreshChanges = () => (state, dispatch) => {
|
|
1513
|
-
dispatch && dispatch(setAction(state.tr,
|
|
1548
|
+
dispatch && dispatch(setAction(state.tr, TrackChangesAction.updateChanges, []));
|
|
1514
1549
|
return true;
|
|
1515
1550
|
};
|
|
1516
1551
|
/**
|
|
@@ -1540,10 +1575,4 @@ var commands = /*#__PURE__*/Object.freeze({
|
|
|
1540
1575
|
setParagraphTestAttribute: setParagraphTestAttribute
|
|
1541
1576
|
});
|
|
1542
1577
|
|
|
1543
|
-
|
|
1544
|
-
exports.enableDebug = enableDebug;
|
|
1545
|
-
exports.getAction = getAction;
|
|
1546
|
-
exports.setAction = setAction;
|
|
1547
|
-
exports.trackChangesPlugin = trackChangesPlugin;
|
|
1548
|
-
exports.trackChangesPluginKey = trackChangesPluginKey;
|
|
1549
|
-
exports.trackCommands = commands;
|
|
1578
|
+
export { CHANGE_OPERATION, CHANGE_STATUS, ChangeSet, TrackChangesStatus, enableDebug, skipTracking, trackChangesPlugin, trackChangesPluginKey, commands as trackCommands };
|