@atlaskit/editor-plugin-collab-edit 7.1.11 → 7.2.1
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/CHANGELOG.md +17 -0
- package/dist/cjs/collabEditPlugin.js +20 -1
- package/dist/cjs/pm-plugins/monitor-organic-changes.js +144 -0
- package/dist/es2019/collabEditPlugin.js +20 -1
- package/dist/es2019/pm-plugins/monitor-organic-changes.js +120 -0
- package/dist/esm/collabEditPlugin.js +20 -1
- package/dist/esm/pm-plugins/monitor-organic-changes.js +137 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/pm-plugins/monitor-organic-changes.d.ts +39 -0
- package/dist/types-ts4.5/index.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/monitor-organic-changes.d.ts +39 -0
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-collab-edit
|
|
2
2
|
|
|
3
|
+
## 7.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`c32dc3155a31a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c32dc3155a31a) -
|
|
8
|
+
Added an organic changes monitor which will reported anayltics when an organic change occurs. The
|
|
9
|
+
new analytic will contain extra information regarding only the transaction which caused the
|
|
10
|
+
organic change.
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
|
|
13
|
+
## 7.2.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- [`c6d3be25ea8e1`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c6d3be25ea8e1) -
|
|
18
|
+
Export LastOrganicChangeMetadata
|
|
19
|
+
|
|
3
20
|
## 7.1.11
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -13,6 +13,7 @@ var _analytics = require("@atlaskit/editor-common/analytics");
|
|
|
13
13
|
var _utils = require("@atlaskit/editor-common/utils");
|
|
14
14
|
var _editorJsonTransformer = require("@atlaskit/editor-json-transformer");
|
|
15
15
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
16
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
16
17
|
var _prosemirrorCollab = require("@atlaskit/prosemirror-collab");
|
|
17
18
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
18
19
|
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
|
|
@@ -22,6 +23,7 @@ var _filterAnalytics = require("./pm-plugins/filterAnalytics");
|
|
|
22
23
|
var _main = require("./pm-plugins/main");
|
|
23
24
|
var _pluginKey = require("./pm-plugins/main/plugin-key");
|
|
24
25
|
var _mergeUnconfirmed = require("./pm-plugins/mergeUnconfirmed");
|
|
26
|
+
var _monitorOrganicChanges = require("./pm-plugins/monitor-organic-changes");
|
|
25
27
|
var _nativeCollabProviderPlugin = require("./pm-plugins/native-collab-provider-plugin");
|
|
26
28
|
var _trackAndFilterSpammingSteps = require("./pm-plugins/track-and-filter-spamming-steps");
|
|
27
29
|
var _trackLastOrganicChange = require("./pm-plugins/track-last-organic-change");
|
|
@@ -190,7 +192,7 @@ var collabEditPlugin = exports.collabEditPlugin = function collabEditPlugin(_ref
|
|
|
190
192
|
return content.every(function (node) {
|
|
191
193
|
try {
|
|
192
194
|
node.check(); // this will throw an error if the node is invalid
|
|
193
|
-
} catch (
|
|
195
|
+
} catch (_unused) {
|
|
194
196
|
return false;
|
|
195
197
|
}
|
|
196
198
|
return true;
|
|
@@ -301,6 +303,23 @@ var collabEditPlugin = exports.collabEditPlugin = function collabEditPlugin(_ref
|
|
|
301
303
|
});
|
|
302
304
|
}
|
|
303
305
|
}));
|
|
306
|
+
if ((0, _platformFeatureFlags.fg)('platform_editor_collab_organic_change_reporting')) {
|
|
307
|
+
(0, _monitorOrganicChanges.monitorOrganic)(_objectSpread(_objectSpread({
|
|
308
|
+
api: api
|
|
309
|
+
}, props), {}, {
|
|
310
|
+
onDataProcessed: function onDataProcessed(data) {
|
|
311
|
+
var _api$analytics4;
|
|
312
|
+
api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 || (_api$analytics4 = _api$analytics4.actions) === null || _api$analytics4 === void 0 || _api$analytics4.fireAnalyticsEvent({
|
|
313
|
+
action: _analytics.ACTION.ORGANIC_CHANGES_TRACKED,
|
|
314
|
+
actionSubject: _analytics.ACTION_SUBJECT.COLLAB,
|
|
315
|
+
attributes: {
|
|
316
|
+
organicChanges: data
|
|
317
|
+
},
|
|
318
|
+
eventType: _analytics.EVENT_TYPE.OPERATIONAL
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
304
323
|
},
|
|
305
324
|
commands: {
|
|
306
325
|
nudgeTelepointer: function nudgeTelepointer(sessionId) {
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.monitorOrganic = exports.getScheduler = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
10
|
+
var _steps = require("@atlaskit/adf-schema/steps");
|
|
11
|
+
var _trackLastOrganicChange = require("./track-last-organic-change");
|
|
12
|
+
var _trackSteps = require("./track-steps");
|
|
13
|
+
var _excluded = ["steps"];
|
|
14
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
15
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
16
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
17
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
18
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
19
|
+
// This is essentially a queue of cached events. When the background task runs to send these items then this queue is flushed.
|
|
20
|
+
var organicReportingCache = [];
|
|
21
|
+
// Every ten seconds we will try to process the step data.
|
|
22
|
+
var LOW_PRIORITY_DELAY = 10000;
|
|
23
|
+
|
|
24
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
25
|
+
|
|
26
|
+
var getScheduler = exports.getScheduler = function getScheduler(obj) {
|
|
27
|
+
if (!obj) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if ('scheduler' in obj) {
|
|
31
|
+
return obj.scheduler;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Processes the steps metadata from the cache and calls the callback function with the processed data.
|
|
38
|
+
*
|
|
39
|
+
* @param {OrganicCacheType} cache - A cache containing steps metadata.
|
|
40
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} onDataProcessed - Callback function to be called with the processed data.
|
|
41
|
+
*/
|
|
42
|
+
var task = function task(cache, onDataProcessed) {
|
|
43
|
+
var data = [];
|
|
44
|
+
var _iterator = _createForOfIteratorHelper(cache),
|
|
45
|
+
_step;
|
|
46
|
+
try {
|
|
47
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
48
|
+
var item = _step.value;
|
|
49
|
+
var steps = item.steps,
|
|
50
|
+
rest = (0, _objectWithoutProperties2.default)(item, _excluded);
|
|
51
|
+
// We'll use the same grouping and sanitize logic from the track-steps util
|
|
52
|
+
var stepTypesAmount = (0, _trackSteps.groupSteps)(steps.map(_trackSteps.sanitizeStep));
|
|
53
|
+
data.push(_objectSpread(_objectSpread({}, rest), {}, {
|
|
54
|
+
stepTypesAmount: stepTypesAmount
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// clear the cache.
|
|
59
|
+
} catch (err) {
|
|
60
|
+
_iterator.e(err);
|
|
61
|
+
} finally {
|
|
62
|
+
_iterator.f();
|
|
63
|
+
}
|
|
64
|
+
cache.length = 0;
|
|
65
|
+
if (data.length > 0) {
|
|
66
|
+
onDataProcessed(data);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Tracks the steps sent by the client by storing them in a cache and scheduling a task to process them. Once the steps are processed, the onDataProcessed callabck will be called.
|
|
72
|
+
*
|
|
73
|
+
* This is a non-critical code. If the browser doesn't support the Scheduler API https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
74
|
+
*
|
|
75
|
+
* @param {TrackProps} props - The properties required for tracking steps.
|
|
76
|
+
* @param {ExtractInjectionAPI<CollabEditPlugin> | undefined} props.api - The API for the CollabEdit plugin.
|
|
77
|
+
* @param {EditorState} props.newEditorState - The new editor state.
|
|
78
|
+
* @param {Readonly<Transaction[]>} props.transactions - The transactions that contain the steps.
|
|
79
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} props.onDataProcessed - Callback function to be called with the processed data.
|
|
80
|
+
*/
|
|
81
|
+
var monitorOrganic = exports.monitorOrganic = function monitorOrganic(_ref) {
|
|
82
|
+
var newEditorState = _ref.newEditorState,
|
|
83
|
+
oldEditorState = _ref.oldEditorState,
|
|
84
|
+
transactions = _ref.transactions,
|
|
85
|
+
onDataProcessed = _ref.onDataProcessed;
|
|
86
|
+
// We can exclude analytic steps since they should never trigger an organic change.
|
|
87
|
+
var newSteps = transactions.flatMap(function (t) {
|
|
88
|
+
return t.steps;
|
|
89
|
+
}).filter(function (step) {
|
|
90
|
+
return !(step instanceof _steps.AnalyticsStep);
|
|
91
|
+
});
|
|
92
|
+
var scheduler = getScheduler(window);
|
|
93
|
+
if (!newSteps.length || !scheduler) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// We know that an organic change during startup will trigger a draft sync which will;
|
|
98
|
+
// fire editor edited event -> confluence/next/packages/editor-features/src/hooks/useDraftSync/useDraftSync.tsx
|
|
99
|
+
// and call triggerUpdate() notifying the BE that user edited the page -> confluence/next/packages/editor-features/src/hooks/useDraftSync/useEditorDraftSyncAction.tsx
|
|
100
|
+
// This can cause a problem with statsig metrics if organic changes are being incorrectly reported, ie an automated change
|
|
101
|
+
// occurs which contributes the user towards editing a page when in fact they didn't edit the page.
|
|
102
|
+
//
|
|
103
|
+
var oldPluginState = _trackLastOrganicChange.trackLastOrganicChangePluginKey.getState(oldEditorState);
|
|
104
|
+
var newPluginState = _trackLastOrganicChange.trackLastOrganicChangePluginKey.getState(newEditorState);
|
|
105
|
+
if (newSteps.length && (oldPluginState === null || oldPluginState === void 0 ? void 0 : oldPluginState.lastLocalOrganicBodyChangeAt) !== (newPluginState === null || newPluginState === void 0 ? void 0 : newPluginState.lastLocalOrganicBodyChangeAt)) {
|
|
106
|
+
var now = Date.now();
|
|
107
|
+
var isFirstChange = !(oldPluginState !== null && oldPluginState !== void 0 && oldPluginState.lastLocalOrganicBodyChangeAt);
|
|
108
|
+
|
|
109
|
+
// Check if we should compact with the previous entry (within 2 seconds) this means with a possible 10sec delay
|
|
110
|
+
// due to the postTask we could have a potential 5 grouped organic changes listed.
|
|
111
|
+
var shouldCompact = organicReportingCache.length > 0 && now - organicReportingCache[organicReportingCache.length - 1].startedAt < 2000;
|
|
112
|
+
if (shouldCompact) {
|
|
113
|
+
// Compact with previous entry
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
115
|
+
var prev = organicReportingCache.pop(); // We know it exists due to shouldCompact check
|
|
116
|
+
|
|
117
|
+
// tr.docChanged
|
|
118
|
+
organicReportingCache.push({
|
|
119
|
+
transactions: prev.transactions + 1,
|
|
120
|
+
startedAt: prev.startedAt,
|
|
121
|
+
endedAt: now,
|
|
122
|
+
isFirstChange: prev.isFirstChange || isFirstChange,
|
|
123
|
+
steps: prev.steps.concat(newSteps)
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
// Add new entry
|
|
127
|
+
organicReportingCache.push({
|
|
128
|
+
transactions: 1,
|
|
129
|
+
startedAt: now,
|
|
130
|
+
endedAt: now,
|
|
131
|
+
isFirstChange: isFirstChange,
|
|
132
|
+
steps: newSteps
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (organicReportingCache.length === 1) {
|
|
136
|
+
scheduler.postTask(function () {
|
|
137
|
+
task(organicReportingCache, onDataProcessed);
|
|
138
|
+
}, {
|
|
139
|
+
priority: 'background',
|
|
140
|
+
delay: LOW_PRIORITY_DELAY
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
@@ -2,6 +2,7 @@ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/anal
|
|
|
2
2
|
import { isEmptyDocument } from '@atlaskit/editor-common/utils';
|
|
3
3
|
import { JSONTransformer } from '@atlaskit/editor-json-transformer';
|
|
4
4
|
import { AddMarkStep, AddNodeMarkStep } from '@atlaskit/editor-prosemirror/transform';
|
|
5
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
6
|
import { collab, getCollabState, sendableSteps } from '@atlaskit/prosemirror-collab';
|
|
6
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
7
8
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
@@ -11,6 +12,7 @@ import { filterAnalyticsSteps } from './pm-plugins/filterAnalytics';
|
|
|
11
12
|
import { createPlugin } from './pm-plugins/main';
|
|
12
13
|
import { pluginKey as mainPluginKey } from './pm-plugins/main/plugin-key';
|
|
13
14
|
import { mergeUnconfirmedSteps } from './pm-plugins/mergeUnconfirmed';
|
|
15
|
+
import { monitorOrganic } from './pm-plugins/monitor-organic-changes';
|
|
14
16
|
import { nativeCollabProviderPlugin } from './pm-plugins/native-collab-provider-plugin';
|
|
15
17
|
import { sanitizeFilteredStep, createPlugin as trackSpammingStepsPlugin } from './pm-plugins/track-and-filter-spamming-steps';
|
|
16
18
|
import { createPlugin as createLastOrganicChangePlugin, trackLastOrganicChangePluginKey } from './pm-plugins/track-last-organic-change';
|
|
@@ -146,7 +148,7 @@ export const collabEditPlugin = ({
|
|
|
146
148
|
return content.every(node => {
|
|
147
149
|
try {
|
|
148
150
|
node.check(); // this will throw an error if the node is invalid
|
|
149
|
-
} catch
|
|
151
|
+
} catch {
|
|
150
152
|
return false;
|
|
151
153
|
}
|
|
152
154
|
return true;
|
|
@@ -249,6 +251,23 @@ export const collabEditPlugin = ({
|
|
|
249
251
|
});
|
|
250
252
|
}
|
|
251
253
|
});
|
|
254
|
+
if (fg('platform_editor_collab_organic_change_reporting')) {
|
|
255
|
+
monitorOrganic({
|
|
256
|
+
api,
|
|
257
|
+
...props,
|
|
258
|
+
onDataProcessed: data => {
|
|
259
|
+
var _api$analytics4, _api$analytics4$actio;
|
|
260
|
+
api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : (_api$analytics4$actio = _api$analytics4.actions) === null || _api$analytics4$actio === void 0 ? void 0 : _api$analytics4$actio.fireAnalyticsEvent({
|
|
261
|
+
action: ACTION.ORGANIC_CHANGES_TRACKED,
|
|
262
|
+
actionSubject: ACTION_SUBJECT.COLLAB,
|
|
263
|
+
attributes: {
|
|
264
|
+
organicChanges: data
|
|
265
|
+
},
|
|
266
|
+
eventType: EVENT_TYPE.OPERATIONAL
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
252
271
|
},
|
|
253
272
|
commands: {
|
|
254
273
|
nudgeTelepointer: sessionId => ({
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
|
|
2
|
+
import { trackLastOrganicChangePluginKey } from './track-last-organic-change';
|
|
3
|
+
import { groupSteps, sanitizeStep } from './track-steps';
|
|
4
|
+
// This is essentially a queue of cached events. When the background task runs to send these items then this queue is flushed.
|
|
5
|
+
const organicReportingCache = [];
|
|
6
|
+
// Every ten seconds we will try to process the step data.
|
|
7
|
+
const LOW_PRIORITY_DELAY = 10000;
|
|
8
|
+
|
|
9
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
10
|
+
|
|
11
|
+
export const getScheduler = obj => {
|
|
12
|
+
if (!obj) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if ('scheduler' in obj) {
|
|
16
|
+
return obj.scheduler;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Processes the steps metadata from the cache and calls the callback function with the processed data.
|
|
23
|
+
*
|
|
24
|
+
* @param {OrganicCacheType} cache - A cache containing steps metadata.
|
|
25
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} onDataProcessed - Callback function to be called with the processed data.
|
|
26
|
+
*/
|
|
27
|
+
const task = (cache, onDataProcessed) => {
|
|
28
|
+
const data = [];
|
|
29
|
+
for (const item of cache) {
|
|
30
|
+
const {
|
|
31
|
+
steps,
|
|
32
|
+
...rest
|
|
33
|
+
} = item;
|
|
34
|
+
// We'll use the same grouping and sanitize logic from the track-steps util
|
|
35
|
+
const stepTypesAmount = groupSteps(steps.map(sanitizeStep));
|
|
36
|
+
data.push({
|
|
37
|
+
...rest,
|
|
38
|
+
stepTypesAmount
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// clear the cache.
|
|
43
|
+
cache.length = 0;
|
|
44
|
+
if (data.length > 0) {
|
|
45
|
+
onDataProcessed(data);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Tracks the steps sent by the client by storing them in a cache and scheduling a task to process them. Once the steps are processed, the onDataProcessed callabck will be called.
|
|
51
|
+
*
|
|
52
|
+
* This is a non-critical code. If the browser doesn't support the Scheduler API https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
53
|
+
*
|
|
54
|
+
* @param {TrackProps} props - The properties required for tracking steps.
|
|
55
|
+
* @param {ExtractInjectionAPI<CollabEditPlugin> | undefined} props.api - The API for the CollabEdit plugin.
|
|
56
|
+
* @param {EditorState} props.newEditorState - The new editor state.
|
|
57
|
+
* @param {Readonly<Transaction[]>} props.transactions - The transactions that contain the steps.
|
|
58
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} props.onDataProcessed - Callback function to be called with the processed data.
|
|
59
|
+
*/
|
|
60
|
+
export const monitorOrganic = ({
|
|
61
|
+
newEditorState,
|
|
62
|
+
oldEditorState,
|
|
63
|
+
transactions,
|
|
64
|
+
onDataProcessed
|
|
65
|
+
}) => {
|
|
66
|
+
// We can exclude analytic steps since they should never trigger an organic change.
|
|
67
|
+
const newSteps = transactions.flatMap(t => t.steps).filter(step => !(step instanceof AnalyticsStep));
|
|
68
|
+
const scheduler = getScheduler(window);
|
|
69
|
+
if (!newSteps.length || !scheduler) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// We know that an organic change during startup will trigger a draft sync which will;
|
|
74
|
+
// fire editor edited event -> confluence/next/packages/editor-features/src/hooks/useDraftSync/useDraftSync.tsx
|
|
75
|
+
// and call triggerUpdate() notifying the BE that user edited the page -> confluence/next/packages/editor-features/src/hooks/useDraftSync/useEditorDraftSyncAction.tsx
|
|
76
|
+
// This can cause a problem with statsig metrics if organic changes are being incorrectly reported, ie an automated change
|
|
77
|
+
// occurs which contributes the user towards editing a page when in fact they didn't edit the page.
|
|
78
|
+
//
|
|
79
|
+
const oldPluginState = trackLastOrganicChangePluginKey.getState(oldEditorState);
|
|
80
|
+
const newPluginState = trackLastOrganicChangePluginKey.getState(newEditorState);
|
|
81
|
+
if (newSteps.length && (oldPluginState === null || oldPluginState === void 0 ? void 0 : oldPluginState.lastLocalOrganicBodyChangeAt) !== (newPluginState === null || newPluginState === void 0 ? void 0 : newPluginState.lastLocalOrganicBodyChangeAt)) {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const isFirstChange = !(oldPluginState !== null && oldPluginState !== void 0 && oldPluginState.lastLocalOrganicBodyChangeAt);
|
|
84
|
+
|
|
85
|
+
// Check if we should compact with the previous entry (within 2 seconds) this means with a possible 10sec delay
|
|
86
|
+
// due to the postTask we could have a potential 5 grouped organic changes listed.
|
|
87
|
+
const shouldCompact = organicReportingCache.length > 0 && now - organicReportingCache[organicReportingCache.length - 1].startedAt < 2000;
|
|
88
|
+
if (shouldCompact) {
|
|
89
|
+
// Compact with previous entry
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
91
|
+
const prev = organicReportingCache.pop(); // We know it exists due to shouldCompact check
|
|
92
|
+
|
|
93
|
+
// tr.docChanged
|
|
94
|
+
organicReportingCache.push({
|
|
95
|
+
transactions: prev.transactions + 1,
|
|
96
|
+
startedAt: prev.startedAt,
|
|
97
|
+
endedAt: now,
|
|
98
|
+
isFirstChange: prev.isFirstChange || isFirstChange,
|
|
99
|
+
steps: prev.steps.concat(newSteps)
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
// Add new entry
|
|
103
|
+
organicReportingCache.push({
|
|
104
|
+
transactions: 1,
|
|
105
|
+
startedAt: now,
|
|
106
|
+
endedAt: now,
|
|
107
|
+
isFirstChange,
|
|
108
|
+
steps: newSteps
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (organicReportingCache.length === 1) {
|
|
112
|
+
scheduler.postTask(() => {
|
|
113
|
+
task(organicReportingCache, onDataProcessed);
|
|
114
|
+
}, {
|
|
115
|
+
priority: 'background',
|
|
116
|
+
delay: LOW_PRIORITY_DELAY
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
@@ -8,6 +8,7 @@ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/anal
|
|
|
8
8
|
import { isEmptyDocument } from '@atlaskit/editor-common/utils';
|
|
9
9
|
import { JSONTransformer } from '@atlaskit/editor-json-transformer';
|
|
10
10
|
import { AddMarkStep, AddNodeMarkStep } from '@atlaskit/editor-prosemirror/transform';
|
|
11
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
12
|
import { collab, getCollabState, sendableSteps } from '@atlaskit/prosemirror-collab';
|
|
12
13
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
13
14
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
@@ -17,6 +18,7 @@ import { filterAnalyticsSteps } from './pm-plugins/filterAnalytics';
|
|
|
17
18
|
import { createPlugin } from './pm-plugins/main';
|
|
18
19
|
import { pluginKey as mainPluginKey } from './pm-plugins/main/plugin-key';
|
|
19
20
|
import { mergeUnconfirmedSteps } from './pm-plugins/mergeUnconfirmed';
|
|
21
|
+
import { monitorOrganic } from './pm-plugins/monitor-organic-changes';
|
|
20
22
|
import { nativeCollabProviderPlugin } from './pm-plugins/native-collab-provider-plugin';
|
|
21
23
|
import { sanitizeFilteredStep, createPlugin as trackSpammingStepsPlugin } from './pm-plugins/track-and-filter-spamming-steps';
|
|
22
24
|
import { createPlugin as createLastOrganicChangePlugin, trackLastOrganicChangePluginKey } from './pm-plugins/track-last-organic-change';
|
|
@@ -183,7 +185,7 @@ export var collabEditPlugin = function collabEditPlugin(_ref4) {
|
|
|
183
185
|
return content.every(function (node) {
|
|
184
186
|
try {
|
|
185
187
|
node.check(); // this will throw an error if the node is invalid
|
|
186
|
-
} catch (
|
|
188
|
+
} catch (_unused) {
|
|
187
189
|
return false;
|
|
188
190
|
}
|
|
189
191
|
return true;
|
|
@@ -294,6 +296,23 @@ export var collabEditPlugin = function collabEditPlugin(_ref4) {
|
|
|
294
296
|
});
|
|
295
297
|
}
|
|
296
298
|
}));
|
|
299
|
+
if (fg('platform_editor_collab_organic_change_reporting')) {
|
|
300
|
+
monitorOrganic(_objectSpread(_objectSpread({
|
|
301
|
+
api: api
|
|
302
|
+
}, props), {}, {
|
|
303
|
+
onDataProcessed: function onDataProcessed(data) {
|
|
304
|
+
var _api$analytics4;
|
|
305
|
+
api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 || (_api$analytics4 = _api$analytics4.actions) === null || _api$analytics4 === void 0 || _api$analytics4.fireAnalyticsEvent({
|
|
306
|
+
action: ACTION.ORGANIC_CHANGES_TRACKED,
|
|
307
|
+
actionSubject: ACTION_SUBJECT.COLLAB,
|
|
308
|
+
attributes: {
|
|
309
|
+
organicChanges: data
|
|
310
|
+
},
|
|
311
|
+
eventType: EVENT_TYPE.OPERATIONAL
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
297
316
|
},
|
|
298
317
|
commands: {
|
|
299
318
|
nudgeTelepointer: function nudgeTelepointer(sessionId) {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
+
var _excluded = ["steps"];
|
|
4
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
7
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
8
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
9
|
+
import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
|
|
10
|
+
import { trackLastOrganicChangePluginKey } from './track-last-organic-change';
|
|
11
|
+
import { groupSteps, sanitizeStep } from './track-steps';
|
|
12
|
+
// This is essentially a queue of cached events. When the background task runs to send these items then this queue is flushed.
|
|
13
|
+
var organicReportingCache = [];
|
|
14
|
+
// Every ten seconds we will try to process the step data.
|
|
15
|
+
var LOW_PRIORITY_DELAY = 10000;
|
|
16
|
+
|
|
17
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
18
|
+
|
|
19
|
+
export var getScheduler = function getScheduler(obj) {
|
|
20
|
+
if (!obj) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if ('scheduler' in obj) {
|
|
24
|
+
return obj.scheduler;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Processes the steps metadata from the cache and calls the callback function with the processed data.
|
|
31
|
+
*
|
|
32
|
+
* @param {OrganicCacheType} cache - A cache containing steps metadata.
|
|
33
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} onDataProcessed - Callback function to be called with the processed data.
|
|
34
|
+
*/
|
|
35
|
+
var task = function task(cache, onDataProcessed) {
|
|
36
|
+
var data = [];
|
|
37
|
+
var _iterator = _createForOfIteratorHelper(cache),
|
|
38
|
+
_step;
|
|
39
|
+
try {
|
|
40
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
41
|
+
var item = _step.value;
|
|
42
|
+
var steps = item.steps,
|
|
43
|
+
rest = _objectWithoutProperties(item, _excluded);
|
|
44
|
+
// We'll use the same grouping and sanitize logic from the track-steps util
|
|
45
|
+
var stepTypesAmount = groupSteps(steps.map(sanitizeStep));
|
|
46
|
+
data.push(_objectSpread(_objectSpread({}, rest), {}, {
|
|
47
|
+
stepTypesAmount: stepTypesAmount
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// clear the cache.
|
|
52
|
+
} catch (err) {
|
|
53
|
+
_iterator.e(err);
|
|
54
|
+
} finally {
|
|
55
|
+
_iterator.f();
|
|
56
|
+
}
|
|
57
|
+
cache.length = 0;
|
|
58
|
+
if (data.length > 0) {
|
|
59
|
+
onDataProcessed(data);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Tracks the steps sent by the client by storing them in a cache and scheduling a task to process them. Once the steps are processed, the onDataProcessed callabck will be called.
|
|
65
|
+
*
|
|
66
|
+
* This is a non-critical code. If the browser doesn't support the Scheduler API https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
67
|
+
*
|
|
68
|
+
* @param {TrackProps} props - The properties required for tracking steps.
|
|
69
|
+
* @param {ExtractInjectionAPI<CollabEditPlugin> | undefined} props.api - The API for the CollabEdit plugin.
|
|
70
|
+
* @param {EditorState} props.newEditorState - The new editor state.
|
|
71
|
+
* @param {Readonly<Transaction[]>} props.transactions - The transactions that contain the steps.
|
|
72
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} props.onDataProcessed - Callback function to be called with the processed data.
|
|
73
|
+
*/
|
|
74
|
+
export var monitorOrganic = function monitorOrganic(_ref) {
|
|
75
|
+
var newEditorState = _ref.newEditorState,
|
|
76
|
+
oldEditorState = _ref.oldEditorState,
|
|
77
|
+
transactions = _ref.transactions,
|
|
78
|
+
onDataProcessed = _ref.onDataProcessed;
|
|
79
|
+
// We can exclude analytic steps since they should never trigger an organic change.
|
|
80
|
+
var newSteps = transactions.flatMap(function (t) {
|
|
81
|
+
return t.steps;
|
|
82
|
+
}).filter(function (step) {
|
|
83
|
+
return !(step instanceof AnalyticsStep);
|
|
84
|
+
});
|
|
85
|
+
var scheduler = getScheduler(window);
|
|
86
|
+
if (!newSteps.length || !scheduler) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// We know that an organic change during startup will trigger a draft sync which will;
|
|
91
|
+
// fire editor edited event -> confluence/next/packages/editor-features/src/hooks/useDraftSync/useDraftSync.tsx
|
|
92
|
+
// and call triggerUpdate() notifying the BE that user edited the page -> confluence/next/packages/editor-features/src/hooks/useDraftSync/useEditorDraftSyncAction.tsx
|
|
93
|
+
// This can cause a problem with statsig metrics if organic changes are being incorrectly reported, ie an automated change
|
|
94
|
+
// occurs which contributes the user towards editing a page when in fact they didn't edit the page.
|
|
95
|
+
//
|
|
96
|
+
var oldPluginState = trackLastOrganicChangePluginKey.getState(oldEditorState);
|
|
97
|
+
var newPluginState = trackLastOrganicChangePluginKey.getState(newEditorState);
|
|
98
|
+
if (newSteps.length && (oldPluginState === null || oldPluginState === void 0 ? void 0 : oldPluginState.lastLocalOrganicBodyChangeAt) !== (newPluginState === null || newPluginState === void 0 ? void 0 : newPluginState.lastLocalOrganicBodyChangeAt)) {
|
|
99
|
+
var now = Date.now();
|
|
100
|
+
var isFirstChange = !(oldPluginState !== null && oldPluginState !== void 0 && oldPluginState.lastLocalOrganicBodyChangeAt);
|
|
101
|
+
|
|
102
|
+
// Check if we should compact with the previous entry (within 2 seconds) this means with a possible 10sec delay
|
|
103
|
+
// due to the postTask we could have a potential 5 grouped organic changes listed.
|
|
104
|
+
var shouldCompact = organicReportingCache.length > 0 && now - organicReportingCache[organicReportingCache.length - 1].startedAt < 2000;
|
|
105
|
+
if (shouldCompact) {
|
|
106
|
+
// Compact with previous entry
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
108
|
+
var prev = organicReportingCache.pop(); // We know it exists due to shouldCompact check
|
|
109
|
+
|
|
110
|
+
// tr.docChanged
|
|
111
|
+
organicReportingCache.push({
|
|
112
|
+
transactions: prev.transactions + 1,
|
|
113
|
+
startedAt: prev.startedAt,
|
|
114
|
+
endedAt: now,
|
|
115
|
+
isFirstChange: prev.isFirstChange || isFirstChange,
|
|
116
|
+
steps: prev.steps.concat(newSteps)
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
// Add new entry
|
|
120
|
+
organicReportingCache.push({
|
|
121
|
+
transactions: 1,
|
|
122
|
+
startedAt: now,
|
|
123
|
+
endedAt: now,
|
|
124
|
+
isFirstChange: isFirstChange,
|
|
125
|
+
steps: newSteps
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (organicReportingCache.length === 1) {
|
|
129
|
+
scheduler.postTask(function () {
|
|
130
|
+
task(organicReportingCache, onDataProcessed);
|
|
131
|
+
}, {
|
|
132
|
+
priority: 'background',
|
|
133
|
+
delay: LOW_PRIORITY_DELAY
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { collabEditPlugin } from './collabEditPlugin';
|
|
2
2
|
export type { CollabEditPlugin, CollabEditPluginOptions, CollabEditPluginDependencies, } from './collabEditPluginType';
|
|
3
|
-
export type { CollabInitializedMetadata, CollabEditPluginSharedState, ReadOnlyParticipants, PrivateCollabEditOptions, CollabSendableSteps, } from './types';
|
|
3
|
+
export type { CollabInitializedMetadata, LastOrganicChangeMetadata, CollabEditPluginSharedState, ReadOnlyParticipants, PrivateCollabEditOptions, CollabSendableSteps, } from './types';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
+
import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import type { CollabEditPlugin } from '../collabEditPluginType';
|
|
4
|
+
export type OrganicMetadataAnalytics = {
|
|
5
|
+
endedAt: number;
|
|
6
|
+
isFirstChange: boolean;
|
|
7
|
+
startedAt: number;
|
|
8
|
+
stepTypesAmount: {
|
|
9
|
+
[key: string]: number;
|
|
10
|
+
};
|
|
11
|
+
transactions: number;
|
|
12
|
+
};
|
|
13
|
+
type TrackProps = {
|
|
14
|
+
api: ExtractInjectionAPI<CollabEditPlugin> | undefined;
|
|
15
|
+
newEditorState: EditorState;
|
|
16
|
+
oldEditorState: EditorState;
|
|
17
|
+
onDataProcessed: (data: OrganicMetadataAnalytics[]) => void;
|
|
18
|
+
transactions: Readonly<Transaction[]>;
|
|
19
|
+
};
|
|
20
|
+
type Scheduler = {
|
|
21
|
+
postTask: (cb: () => void, options: {
|
|
22
|
+
delay: number;
|
|
23
|
+
priority: 'background';
|
|
24
|
+
}) => Promise<unknown>;
|
|
25
|
+
};
|
|
26
|
+
export declare const getScheduler: (obj: any) => Scheduler | null;
|
|
27
|
+
/**
|
|
28
|
+
* Tracks the steps sent by the client by storing them in a cache and scheduling a task to process them. Once the steps are processed, the onDataProcessed callabck will be called.
|
|
29
|
+
*
|
|
30
|
+
* This is a non-critical code. If the browser doesn't support the Scheduler API https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
31
|
+
*
|
|
32
|
+
* @param {TrackProps} props - The properties required for tracking steps.
|
|
33
|
+
* @param {ExtractInjectionAPI<CollabEditPlugin> | undefined} props.api - The API for the CollabEdit plugin.
|
|
34
|
+
* @param {EditorState} props.newEditorState - The new editor state.
|
|
35
|
+
* @param {Readonly<Transaction[]>} props.transactions - The transactions that contain the steps.
|
|
36
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} props.onDataProcessed - Callback function to be called with the processed data.
|
|
37
|
+
*/
|
|
38
|
+
export declare const monitorOrganic: ({ newEditorState, oldEditorState, transactions, onDataProcessed, }: TrackProps) => void;
|
|
39
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { collabEditPlugin } from './collabEditPlugin';
|
|
2
2
|
export type { CollabEditPlugin, CollabEditPluginOptions, CollabEditPluginDependencies, } from './collabEditPluginType';
|
|
3
|
-
export type { CollabInitializedMetadata, CollabEditPluginSharedState, ReadOnlyParticipants, PrivateCollabEditOptions, CollabSendableSteps, } from './types';
|
|
3
|
+
export type { CollabInitializedMetadata, LastOrganicChangeMetadata, CollabEditPluginSharedState, ReadOnlyParticipants, PrivateCollabEditOptions, CollabSendableSteps, } from './types';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
+
import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import type { CollabEditPlugin } from '../collabEditPluginType';
|
|
4
|
+
export type OrganicMetadataAnalytics = {
|
|
5
|
+
endedAt: number;
|
|
6
|
+
isFirstChange: boolean;
|
|
7
|
+
startedAt: number;
|
|
8
|
+
stepTypesAmount: {
|
|
9
|
+
[key: string]: number;
|
|
10
|
+
};
|
|
11
|
+
transactions: number;
|
|
12
|
+
};
|
|
13
|
+
type TrackProps = {
|
|
14
|
+
api: ExtractInjectionAPI<CollabEditPlugin> | undefined;
|
|
15
|
+
newEditorState: EditorState;
|
|
16
|
+
oldEditorState: EditorState;
|
|
17
|
+
onDataProcessed: (data: OrganicMetadataAnalytics[]) => void;
|
|
18
|
+
transactions: Readonly<Transaction[]>;
|
|
19
|
+
};
|
|
20
|
+
type Scheduler = {
|
|
21
|
+
postTask: (cb: () => void, options: {
|
|
22
|
+
delay: number;
|
|
23
|
+
priority: 'background';
|
|
24
|
+
}) => Promise<unknown>;
|
|
25
|
+
};
|
|
26
|
+
export declare const getScheduler: (obj: any) => Scheduler | null;
|
|
27
|
+
/**
|
|
28
|
+
* Tracks the steps sent by the client by storing them in a cache and scheduling a task to process them. Once the steps are processed, the onDataProcessed callabck will be called.
|
|
29
|
+
*
|
|
30
|
+
* This is a non-critical code. If the browser doesn't support the Scheduler API https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/
|
|
31
|
+
*
|
|
32
|
+
* @param {TrackProps} props - The properties required for tracking steps.
|
|
33
|
+
* @param {ExtractInjectionAPI<CollabEditPlugin> | undefined} props.api - The API for the CollabEdit plugin.
|
|
34
|
+
* @param {EditorState} props.newEditorState - The new editor state.
|
|
35
|
+
* @param {Readonly<Transaction[]>} props.transactions - The transactions that contain the steps.
|
|
36
|
+
* @param {(data: OrganicMetadataAnalytics[]) => void} props.onDataProcessed - Callback function to be called with the processed data.
|
|
37
|
+
*/
|
|
38
|
+
export declare const monitorOrganic: ({ newEditorState, oldEditorState, transactions, onDataProcessed, }: TrackProps) => void;
|
|
39
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-collab-edit",
|
|
3
|
-
"version": "7.1
|
|
3
|
+
"version": "7.2.1",
|
|
4
4
|
"description": "Collab Edit plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
"@atlaskit/frontend-utilities": "^3.2.0",
|
|
40
40
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
41
41
|
"@atlaskit/prosemirror-collab": "^0.22.0",
|
|
42
|
-
"@atlaskit/tmp-editor-statsig": "^15.
|
|
42
|
+
"@atlaskit/tmp-editor-statsig": "^15.15.0",
|
|
43
43
|
"@babel/runtime": "^7.0.0",
|
|
44
44
|
"memoize-one": "^6.0.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@atlaskit/editor-common": "^110.
|
|
47
|
+
"@atlaskit/editor-common": "^110.46.0",
|
|
48
48
|
"react": "^18.2.0",
|
|
49
49
|
"react-dom": "^18.2.0"
|
|
50
50
|
},
|
|
@@ -94,6 +94,9 @@
|
|
|
94
94
|
},
|
|
95
95
|
"platform_editor_inorganic_batchattrsstep_localid": {
|
|
96
96
|
"type": "boolean"
|
|
97
|
+
},
|
|
98
|
+
"platform_editor_collab_organic_change_reporting": {
|
|
99
|
+
"type": "boolean"
|
|
97
100
|
}
|
|
98
101
|
}
|
|
99
102
|
}
|