@atlaskit/editor-plugin-collab-edit 1.11.2 → 1.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlaskit/editor-plugin-collab-edit
2
2
 
3
+ ## 1.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#128347](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/128347)
8
+ [`e33566cebd5d1`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/e33566cebd5d1) -
9
+ [ED-24175] bump @atlaskit/adf-schema to 40.8.1 and @atlassian/adf-schema-json to 1.22.0 to
10
+ promotecodeblocks & media in quotes, and nested expands in expands to full schema, and allow
11
+ quotes in panels and decisions in lists in stage0 schema, and a validator spec change
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies
16
+
17
+ ## 1.12.0
18
+
19
+ ### Minor Changes
20
+
21
+ - [#127757](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/127757)
22
+ [`7ebfdd4e7821d`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/7ebfdd4e7821d) -
23
+ Added logic to track the timestamp of when last organic change happened
24
+
3
25
  ## 1.11.2
4
26
 
5
27
  ### Patch Changes
@@ -11,12 +11,14 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
11
11
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
12
  var _analytics = require("@atlaskit/editor-common/analytics");
13
13
  var _transform = require("@atlaskit/editor-prosemirror/transform");
14
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
15
  var _prosemirrorCollab = require("@atlaskit/prosemirror-collab");
15
16
  var _analytics2 = require("./analytics");
16
17
  var _sendTransaction = require("./events/send-transaction");
17
18
  var _main = require("./pm-plugins/main");
18
19
  var _pluginKey = require("./pm-plugins/main/plugin-key");
19
20
  var _nativeCollabProviderPlugin = require("./pm-plugins/native-collab-provider-plugin");
21
+ var _trackLastOrganicChange = require("./pm-plugins/track-last-organic-change");
20
22
  var _trackNcsInitialization = require("./pm-plugins/track-ncs-initialization");
21
23
  var _trackSteps = require("./track-steps");
22
24
  var _utils = require("./utils");
@@ -109,7 +111,8 @@ var collabEditPlugin = exports.collabEditPlugin = function collabEditPlugin(_ref
109
111
  initialised: {
110
112
  collabInitialisedAt: null,
111
113
  firstChangeAfterInitAt: null,
112
- firstContentBodyChangeAfterInitAt: null
114
+ firstContentBodyChangeAfterInitAt: null,
115
+ lastOrganicChangeAt: null
113
116
  },
114
117
  activeParticipants: undefined,
115
118
  sessionId: undefined
@@ -117,13 +120,15 @@ var collabEditPlugin = exports.collabEditPlugin = function collabEditPlugin(_ref
117
120
  }
118
121
  var collabPluginState = _pluginKey.pluginKey.getState(state);
119
122
  var metadata = _trackNcsInitialization.trackNCSInitializationPluginKey.getState(state);
123
+ var lastOrganicChangeState = _trackLastOrganicChange.trackLastOrganicChangePluginKey.getState(state);
120
124
  return {
121
125
  activeParticipants: collabPluginState === null || collabPluginState === void 0 ? void 0 : collabPluginState.activeParticipants,
122
126
  sessionId: collabPluginState === null || collabPluginState === void 0 ? void 0 : collabPluginState.sessionId,
123
127
  initialised: {
124
128
  collabInitialisedAt: (metadata === null || metadata === void 0 ? void 0 : metadata.collabInitialisedAt) || null,
125
129
  firstChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstChangeAfterInitAt) || null,
126
- firstContentBodyChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstContentBodyChangeAfterInitAt) || null
130
+ firstContentBodyChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstContentBodyChangeAfterInitAt) || null,
131
+ lastOrganicChangeAt: (lastOrganicChangeState === null || lastOrganicChangeState === void 0 ? void 0 : lastOrganicChangeState.lastOrganicChangeAt) || null
127
132
  }
128
133
  };
129
134
  },
@@ -141,7 +146,7 @@ var collabEditPlugin = exports.collabEditPlugin = function collabEditPlugin(_ref
141
146
  useNativePlugin = _ref5$useNativePlugin === void 0 ? false : _ref5$useNativePlugin,
142
147
  _ref5$userId = _ref5.userId,
143
148
  userId = _ref5$userId === void 0 ? null : _ref5$userId;
144
- return [].concat((0, _toConsumableArray2.default)(useNativePlugin ? [{
149
+ var plugins = [].concat((0, _toConsumableArray2.default)(useNativePlugin ? [{
145
150
  name: 'pmCollab',
146
151
  plugin: function plugin() {
147
152
  return (0, _prosemirrorCollab.collab)({
@@ -166,6 +171,13 @@ var collabEditPlugin = exports.collabEditPlugin = function collabEditPlugin(_ref
166
171
  name: 'collabTrackNCSInitializationPlugin',
167
172
  plugin: _trackNcsInitialization.createPlugin
168
173
  }]);
174
+ if ((0, _platformFeatureFlags.fg)('platform_editor_last_organic_change')) {
175
+ plugins.push({
176
+ name: 'collabTrackLastOrganicChangePlugin',
177
+ plugin: _trackLastOrganicChange.createPlugin
178
+ });
179
+ }
180
+ return plugins;
169
181
  },
170
182
  onEditorViewStateUpdated: function onEditorViewStateUpdated(props) {
171
183
  var _api$analytics, _api$editorViewMode, _options$useNativePlu;
@@ -18,13 +18,9 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
18
18
  var _collab = require("@atlaskit/editor-common/collab");
19
19
  var _utils = require("@atlaskit/editor-common/utils");
20
20
  var _state = require("@atlaskit/editor-prosemirror/state");
21
- var _transform = require("@atlaskit/editor-prosemirror/transform");
22
21
  var _view = require("@atlaskit/editor-prosemirror/view");
23
22
  var _participants = require("../../participants");
24
23
  var _utils2 = require("../../utils");
25
- var isReplaceStep = function isReplaceStep(step) {
26
- return step instanceof _transform.ReplaceStep;
27
- };
28
24
  /**
29
25
  * Returns position where it's possible to place a decoration.
30
26
  */
@@ -149,7 +145,7 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
149
145
  // Reapplies decorators those got removed by the state change
150
146
  onRemove: function onRemove(spec) {
151
147
  if (spec.pointer && spec.pointer.sessionId && spec.key === "telepointer-".concat(spec.pointer.sessionId)) {
152
- var step = tr.steps.filter(isReplaceStep)[0];
148
+ var step = tr.steps.filter(_utils2.isReplaceStep)[0];
153
149
  if (step) {
154
150
  var _sessionId = spec.pointer.sessionId;
155
151
  var _ref = step,
@@ -167,7 +163,7 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
167
163
 
168
164
  // Remove any selection decoration within the change range,
169
165
  // takes care of the issue when after pasting we end up with a dead selection
170
- tr.steps.filter(isReplaceStep).forEach(function (s) {
166
+ tr.steps.filter(_utils2.isReplaceStep).forEach(function (s) {
171
167
  var _ref2 = s,
172
168
  from = _ref2.from,
173
169
  to = _ref2.to;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.trackLastOrganicChangePluginKey = exports.createPlugin = void 0;
7
+ var _collab = require("@atlaskit/editor-common/collab");
8
+ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
9
+ var _state = require("@atlaskit/editor-prosemirror/state");
10
+ var _utils = require("../utils");
11
+ var trackLastOrganicChangePluginKey = exports.trackLastOrganicChangePluginKey = new _state.PluginKey('collabTrackLastOrganicChangePlugin');
12
+ var createPlugin = exports.createPlugin = function createPlugin() {
13
+ return new _safePlugin.SafePlugin({
14
+ key: trackLastOrganicChangePluginKey,
15
+ state: {
16
+ init: function init() {
17
+ return {
18
+ lastOrganicChangeAt: null
19
+ };
20
+ },
21
+ apply: function apply(transaction, prevPluginState) {
22
+ var isRemote = (0, _utils.originalTransactionHasMeta)(transaction, 'isRemote');
23
+ var isDocumentReplaceFromRemote = isRemote && (0, _utils.originalTransactionHasMeta)(transaction, 'replaceDocument');
24
+ if (isDocumentReplaceFromRemote) {
25
+ return prevPluginState;
26
+ }
27
+ if (isRemote || (0, _collab.isDirtyTransaction)(transaction)) {
28
+ return prevPluginState;
29
+ }
30
+ if ((0, _utils.isOrganicChange)(transaction)) {
31
+ return {
32
+ lastOrganicChangeAt: Date.now()
33
+ };
34
+ }
35
+ return prevPluginState;
36
+ }
37
+ }
38
+ });
39
+ };
@@ -10,18 +10,8 @@ var _collab = require("@atlaskit/editor-common/collab");
10
10
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
11
11
  var _state = require("@atlaskit/editor-prosemirror/state");
12
12
  var _transform = require("@atlaskit/editor-prosemirror/transform");
13
+ var _utils = require("../utils");
13
14
  var trackNCSInitializationPluginKey = exports.trackNCSInitializationPluginKey = new _state.PluginKey('collabTrackNCSInitializationPlugin');
14
- var originalTransactionHasMeta = function originalTransactionHasMeta(transaction, metaTag) {
15
- var hasMetaTag = Boolean(transaction.getMeta(metaTag));
16
- if (hasMetaTag) {
17
- return true;
18
- }
19
- var appendedTransaction = transaction.getMeta('appendedTransaction');
20
- if (appendedTransaction instanceof _state.Transaction) {
21
- return originalTransactionHasMeta(appendedTransaction, metaTag);
22
- }
23
- return false;
24
- };
25
15
  var createPlugin = exports.createPlugin = function createPlugin() {
26
16
  return new _safePlugin.SafePlugin({
27
17
  key: trackNCSInitializationPluginKey,
@@ -45,8 +35,8 @@ var createPlugin = exports.createPlugin = function createPlugin() {
45
35
  if (!shouldCheckDocument) {
46
36
  return prevPluginState;
47
37
  }
48
- var isRemote = originalTransactionHasMeta(transaction, 'isRemote');
49
- var isDocumentReplaceFromRemote = isRemote && originalTransactionHasMeta(transaction, 'replaceDocument');
38
+ var isRemote = (0, _utils.originalTransactionHasMeta)(transaction, 'isRemote');
39
+ var isDocumentReplaceFromRemote = isRemote && (0, _utils.originalTransactionHasMeta)(transaction, 'replaceDocument');
50
40
  if (isDocumentReplaceFromRemote) {
51
41
  return prevPluginState;
52
42
  }
package/dist/cjs/utils.js CHANGED
@@ -6,11 +6,13 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.findPointers = exports.createTelepointers = void 0;
8
8
  exports.getAvatarColor = getAvatarColor;
9
- exports.scrollToCollabCursor = exports.replaceDocument = exports.getPositionOfTelepointer = void 0;
9
+ exports.scrollToCollabCursor = exports.replaceDocument = exports.originalTransactionHasMeta = exports.isReplaceStep = exports.isOrganicChange = exports.getPositionOfTelepointer = void 0;
10
10
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
11
+ var _steps = require("@atlaskit/adf-schema/steps");
11
12
  var _analytics = require("@atlaskit/editor-common/analytics");
12
13
  var _whitespace = require("@atlaskit/editor-common/whitespace");
13
14
  var _state = require("@atlaskit/editor-prosemirror/state");
15
+ var _transform = require("@atlaskit/editor-prosemirror/transform");
14
16
  var _view = require("@atlaskit/editor-prosemirror/view");
15
17
  var _consts = require("@atlaskit/editor-shared-styles/consts");
16
18
  var findPointers = exports.findPointers = function findPointers(id, decorations) {
@@ -140,4 +142,61 @@ var getPositionOfTelepointer = exports.getPositionOfTelepointer = function getPo
140
142
  }
141
143
  });
142
144
  return scrollPosition;
145
+ };
146
+ var isReplaceStep = exports.isReplaceStep = function isReplaceStep(step) {
147
+ return step instanceof _transform.ReplaceStep;
148
+ };
149
+ var originalTransactionHasMeta = exports.originalTransactionHasMeta = function originalTransactionHasMeta(transaction, metaTag) {
150
+ var hasMetaTag = Boolean(transaction.getMeta(metaTag));
151
+ if (hasMetaTag) {
152
+ return true;
153
+ }
154
+ var appendedTransaction = transaction.getMeta('appendedTransaction');
155
+ if (appendedTransaction instanceof _state.Transaction) {
156
+ return originalTransactionHasMeta(appendedTransaction, metaTag);
157
+ }
158
+ return false;
159
+ };
160
+
161
+ /**
162
+ * This list contains step attributes that do not result from a user action.
163
+ * All steps that contain ONLY the blocked attribute are considered automated steps
164
+ * and should not be recognised as organic change.
165
+ *
166
+ * `attr_colwidth` is an exception to above explanation. Resizing the column
167
+ * currently creates too many steps and is therefore also on this list.
168
+ *
169
+ * Steps analycs dashboard: https://atlassian-discover.cloud.databricks.com/dashboardsv3/01ef4d3c8aa916c8b0cb5332a9f37caf/published?o=4482001201517624
170
+ */
171
+ var blockedAttrsList = ['__contextId', 'localId', '__autoSize', 'attr_colwidth'];
172
+
173
+ /**
174
+ * Takes the transaction and editor state and checks if the transaction is considered organic change
175
+ * @param tr Transaction
176
+ * @returns boolean
177
+ */
178
+ var isOrganicChange = exports.isOrganicChange = function isOrganicChange(tr) {
179
+ // If document has not been marked as `docChanged` by PM, skip the rest of the logic
180
+ if (!tr.docChanged) {
181
+ return false;
182
+ }
183
+ return tr.steps.some(function (step) {
184
+ // If a step is an instance of AnalyticsStep, it is not considered organic
185
+ if (step instanceof _steps.AnalyticsStep) {
186
+ return false;
187
+ }
188
+ // If a step is not an instance of SetAttrsStep, it is considered organic
189
+ if (!(step instanceof _steps.SetAttrsStep)) {
190
+ return true;
191
+ }
192
+ var allAttributes = Object.keys(step.attrs);
193
+ // If a step is an instance of SetAttrsStep, it checks if the attributes in the step
194
+ // are not in the `blockedAttributes`. If one of the attributes not on the list, it considers the change
195
+ // organic but only if the entire document is not equal to the previous state.
196
+ return allAttributes.some(function (attr) {
197
+ if (!blockedAttrsList.includes(attr)) {
198
+ return true;
199
+ }
200
+ }) && !tr.doc.eq(tr.before);
201
+ });
143
202
  };
@@ -1,11 +1,13 @@
1
1
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
2
  import { AddMarkStep, AddNodeMarkStep } from '@atlaskit/editor-prosemirror/transform';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
3
4
  import { collab } from '@atlaskit/prosemirror-collab';
4
5
  import { addSynchronyErrorAnalytics } from './analytics';
5
6
  import { sendTransaction } from './events/send-transaction';
6
7
  import { createPlugin } from './pm-plugins/main';
7
8
  import { pluginKey as mainPluginKey } from './pm-plugins/main/plugin-key';
8
9
  import { nativeCollabProviderPlugin } from './pm-plugins/native-collab-provider-plugin';
10
+ import { createPlugin as createLastOrganicChangePlugin, trackLastOrganicChangePluginKey } from './pm-plugins/track-last-organic-change';
9
11
  import { createPlugin as createTrackNCSInitializationPlugin, trackNCSInitializationPluginKey } from './pm-plugins/track-ncs-initialization';
10
12
  import { track } from './track-steps';
11
13
  import { getAvatarColor } from './utils';
@@ -70,7 +72,8 @@ export const collabEditPlugin = ({
70
72
  initialised: {
71
73
  collabInitialisedAt: null,
72
74
  firstChangeAfterInitAt: null,
73
- firstContentBodyChangeAfterInitAt: null
75
+ firstContentBodyChangeAfterInitAt: null,
76
+ lastOrganicChangeAt: null
74
77
  },
75
78
  activeParticipants: undefined,
76
79
  sessionId: undefined
@@ -78,13 +81,15 @@ export const collabEditPlugin = ({
78
81
  }
79
82
  const collabPluginState = mainPluginKey.getState(state);
80
83
  const metadata = trackNCSInitializationPluginKey.getState(state);
84
+ const lastOrganicChangeState = trackLastOrganicChangePluginKey.getState(state);
81
85
  return {
82
86
  activeParticipants: collabPluginState === null || collabPluginState === void 0 ? void 0 : collabPluginState.activeParticipants,
83
87
  sessionId: collabPluginState === null || collabPluginState === void 0 ? void 0 : collabPluginState.sessionId,
84
88
  initialised: {
85
89
  collabInitialisedAt: (metadata === null || metadata === void 0 ? void 0 : metadata.collabInitialisedAt) || null,
86
90
  firstChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstChangeAfterInitAt) || null,
87
- firstContentBodyChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstContentBodyChangeAfterInitAt) || null
91
+ firstContentBodyChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstContentBodyChangeAfterInitAt) || null,
92
+ lastOrganicChangeAt: (lastOrganicChangeState === null || lastOrganicChangeState === void 0 ? void 0 : lastOrganicChangeState.lastOrganicChangeAt) || null
88
93
  }
89
94
  };
90
95
  },
@@ -99,7 +104,7 @@ export const collabEditPlugin = ({
99
104
  useNativePlugin = false,
100
105
  userId = null
101
106
  } = options || {};
102
- return [...(useNativePlugin ? [{
107
+ const plugins = [...(useNativePlugin ? [{
103
108
  name: 'pmCollab',
104
109
  plugin: () => collab({
105
110
  clientID: userId
@@ -121,6 +126,13 @@ export const collabEditPlugin = ({
121
126
  name: 'collabTrackNCSInitializationPlugin',
122
127
  plugin: createTrackNCSInitializationPlugin
123
128
  }];
129
+ if (fg('platform_editor_last_organic_change')) {
130
+ plugins.push({
131
+ name: 'collabTrackLastOrganicChangePlugin',
132
+ plugin: createLastOrganicChangePlugin
133
+ });
134
+ }
135
+ return plugins;
124
136
  },
125
137
  onEditorViewStateUpdated(props) {
126
138
  var _api$analytics, _api$editorViewMode, _api$editorViewMode$s, _options$useNativePlu;
@@ -2,11 +2,9 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { TELEPOINTER_DIM_CLASS } from '@atlaskit/editor-common/collab';
3
3
  import { browser } from '@atlaskit/editor-common/utils';
4
4
  import { Selection } from '@atlaskit/editor-prosemirror/state';
5
- import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
6
5
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
7
6
  import { Participants } from '../../participants';
8
- import { createTelepointers, findPointers, getPositionOfTelepointer } from '../../utils';
9
- const isReplaceStep = step => step instanceof ReplaceStep;
7
+ import { createTelepointers, findPointers, getPositionOfTelepointer, isReplaceStep } from '../../utils';
10
8
  export { TELEPOINTER_DIM_CLASS };
11
9
 
12
10
  /**
@@ -0,0 +1,33 @@
1
+ import { isDirtyTransaction } from '@atlaskit/editor-common/collab';
2
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
+ import { isOrganicChange, originalTransactionHasMeta } from '../utils';
5
+ export const trackLastOrganicChangePluginKey = new PluginKey('collabTrackLastOrganicChangePlugin');
6
+ export const createPlugin = () => {
7
+ return new SafePlugin({
8
+ key: trackLastOrganicChangePluginKey,
9
+ state: {
10
+ init() {
11
+ return {
12
+ lastOrganicChangeAt: null
13
+ };
14
+ },
15
+ apply(transaction, prevPluginState) {
16
+ const isRemote = originalTransactionHasMeta(transaction, 'isRemote');
17
+ const isDocumentReplaceFromRemote = isRemote && originalTransactionHasMeta(transaction, 'replaceDocument');
18
+ if (isDocumentReplaceFromRemote) {
19
+ return prevPluginState;
20
+ }
21
+ if (isRemote || isDirtyTransaction(transaction)) {
22
+ return prevPluginState;
23
+ }
24
+ if (isOrganicChange(transaction)) {
25
+ return {
26
+ lastOrganicChangeAt: Date.now()
27
+ };
28
+ }
29
+ return prevPluginState;
30
+ }
31
+ }
32
+ });
33
+ };
@@ -1,19 +1,9 @@
1
1
  import { isDirtyTransaction } from '@atlaskit/editor-common/collab';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
- import { PluginKey, Transaction } from '@atlaskit/editor-prosemirror/state';
3
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
4
  import { AddMarkStep } from '@atlaskit/editor-prosemirror/transform';
5
+ import { originalTransactionHasMeta } from '../utils';
5
6
  export const trackNCSInitializationPluginKey = new PluginKey('collabTrackNCSInitializationPlugin');
6
- const originalTransactionHasMeta = (transaction, metaTag) => {
7
- const hasMetaTag = Boolean(transaction.getMeta(metaTag));
8
- if (hasMetaTag) {
9
- return true;
10
- }
11
- const appendedTransaction = transaction.getMeta('appendedTransaction');
12
- if (appendedTransaction instanceof Transaction) {
13
- return originalTransactionHasMeta(appendedTransaction, metaTag);
14
- }
15
- return false;
16
- };
17
7
  export const createPlugin = () => {
18
8
  return new SafePlugin({
19
9
  key: trackNCSInitializationPluginKey,
@@ -1,6 +1,9 @@
1
+ import { AnalyticsStep, SetAttrsStep } from '@atlaskit/adf-schema/steps';
1
2
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
3
  import { ZERO_WIDTH_JOINER } from '@atlaskit/editor-common/whitespace';
4
+ import { Transaction } from '@atlaskit/editor-prosemirror/state';
3
5
  import { Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
6
+ import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
4
7
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
5
8
  import { avatarColors } from '@atlaskit/editor-shared-styles/consts';
6
9
  export const findPointers = (id, decorations) => decorations.find().reduce((arr, deco) => deco.spec.pointer.sessionId === id ? arr.concat(deco) : arr, []);
@@ -128,4 +131,59 @@ export const getPositionOfTelepointer = (sessionId, decorationSet) => {
128
131
  }
129
132
  });
130
133
  return scrollPosition;
134
+ };
135
+ export const isReplaceStep = step => step instanceof ReplaceStep;
136
+ export const originalTransactionHasMeta = (transaction, metaTag) => {
137
+ const hasMetaTag = Boolean(transaction.getMeta(metaTag));
138
+ if (hasMetaTag) {
139
+ return true;
140
+ }
141
+ const appendedTransaction = transaction.getMeta('appendedTransaction');
142
+ if (appendedTransaction instanceof Transaction) {
143
+ return originalTransactionHasMeta(appendedTransaction, metaTag);
144
+ }
145
+ return false;
146
+ };
147
+
148
+ /**
149
+ * This list contains step attributes that do not result from a user action.
150
+ * All steps that contain ONLY the blocked attribute are considered automated steps
151
+ * and should not be recognised as organic change.
152
+ *
153
+ * `attr_colwidth` is an exception to above explanation. Resizing the column
154
+ * currently creates too many steps and is therefore also on this list.
155
+ *
156
+ * Steps analycs dashboard: https://atlassian-discover.cloud.databricks.com/dashboardsv3/01ef4d3c8aa916c8b0cb5332a9f37caf/published?o=4482001201517624
157
+ */
158
+ const blockedAttrsList = ['__contextId', 'localId', '__autoSize', 'attr_colwidth'];
159
+
160
+ /**
161
+ * Takes the transaction and editor state and checks if the transaction is considered organic change
162
+ * @param tr Transaction
163
+ * @returns boolean
164
+ */
165
+ export const isOrganicChange = tr => {
166
+ // If document has not been marked as `docChanged` by PM, skip the rest of the logic
167
+ if (!tr.docChanged) {
168
+ return false;
169
+ }
170
+ return tr.steps.some(step => {
171
+ // If a step is an instance of AnalyticsStep, it is not considered organic
172
+ if (step instanceof AnalyticsStep) {
173
+ return false;
174
+ }
175
+ // If a step is not an instance of SetAttrsStep, it is considered organic
176
+ if (!(step instanceof SetAttrsStep)) {
177
+ return true;
178
+ }
179
+ const allAttributes = Object.keys(step.attrs);
180
+ // If a step is an instance of SetAttrsStep, it checks if the attributes in the step
181
+ // are not in the `blockedAttributes`. If one of the attributes not on the list, it considers the change
182
+ // organic but only if the entire document is not equal to the previous state.
183
+ return allAttributes.some(attr => {
184
+ if (!blockedAttrsList.includes(attr)) {
185
+ return true;
186
+ }
187
+ }) && !tr.doc.eq(tr.before);
188
+ });
131
189
  };
@@ -6,12 +6,14 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
6
6
  import _regeneratorRuntime from "@babel/runtime/regenerator";
7
7
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
8
8
  import { AddMarkStep, AddNodeMarkStep } from '@atlaskit/editor-prosemirror/transform';
9
+ import { fg } from '@atlaskit/platform-feature-flags';
9
10
  import { collab } from '@atlaskit/prosemirror-collab';
10
11
  import { addSynchronyErrorAnalytics } from './analytics';
11
12
  import { sendTransaction } from './events/send-transaction';
12
13
  import { createPlugin } from './pm-plugins/main';
13
14
  import { pluginKey as mainPluginKey } from './pm-plugins/main/plugin-key';
14
15
  import { nativeCollabProviderPlugin } from './pm-plugins/native-collab-provider-plugin';
16
+ import { createPlugin as createLastOrganicChangePlugin, trackLastOrganicChangePluginKey } from './pm-plugins/track-last-organic-change';
15
17
  import { createPlugin as createTrackNCSInitializationPlugin, trackNCSInitializationPluginKey } from './pm-plugins/track-ncs-initialization';
16
18
  import { track } from './track-steps';
17
19
  import { getAvatarColor } from './utils';
@@ -102,7 +104,8 @@ export var collabEditPlugin = function collabEditPlugin(_ref4) {
102
104
  initialised: {
103
105
  collabInitialisedAt: null,
104
106
  firstChangeAfterInitAt: null,
105
- firstContentBodyChangeAfterInitAt: null
107
+ firstContentBodyChangeAfterInitAt: null,
108
+ lastOrganicChangeAt: null
106
109
  },
107
110
  activeParticipants: undefined,
108
111
  sessionId: undefined
@@ -110,13 +113,15 @@ export var collabEditPlugin = function collabEditPlugin(_ref4) {
110
113
  }
111
114
  var collabPluginState = mainPluginKey.getState(state);
112
115
  var metadata = trackNCSInitializationPluginKey.getState(state);
116
+ var lastOrganicChangeState = trackLastOrganicChangePluginKey.getState(state);
113
117
  return {
114
118
  activeParticipants: collabPluginState === null || collabPluginState === void 0 ? void 0 : collabPluginState.activeParticipants,
115
119
  sessionId: collabPluginState === null || collabPluginState === void 0 ? void 0 : collabPluginState.sessionId,
116
120
  initialised: {
117
121
  collabInitialisedAt: (metadata === null || metadata === void 0 ? void 0 : metadata.collabInitialisedAt) || null,
118
122
  firstChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstChangeAfterInitAt) || null,
119
- firstContentBodyChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstContentBodyChangeAfterInitAt) || null
123
+ firstContentBodyChangeAfterInitAt: (metadata === null || metadata === void 0 ? void 0 : metadata.firstContentBodyChangeAfterInitAt) || null,
124
+ lastOrganicChangeAt: (lastOrganicChangeState === null || lastOrganicChangeState === void 0 ? void 0 : lastOrganicChangeState.lastOrganicChangeAt) || null
120
125
  }
121
126
  };
122
127
  },
@@ -134,7 +139,7 @@ export var collabEditPlugin = function collabEditPlugin(_ref4) {
134
139
  useNativePlugin = _ref5$useNativePlugin === void 0 ? false : _ref5$useNativePlugin,
135
140
  _ref5$userId = _ref5.userId,
136
141
  userId = _ref5$userId === void 0 ? null : _ref5$userId;
137
- return [].concat(_toConsumableArray(useNativePlugin ? [{
142
+ var plugins = [].concat(_toConsumableArray(useNativePlugin ? [{
138
143
  name: 'pmCollab',
139
144
  plugin: function plugin() {
140
145
  return collab({
@@ -159,6 +164,13 @@ export var collabEditPlugin = function collabEditPlugin(_ref4) {
159
164
  name: 'collabTrackNCSInitializationPlugin',
160
165
  plugin: createTrackNCSInitializationPlugin
161
166
  }]);
167
+ if (fg('platform_editor_last_organic_change')) {
168
+ plugins.push({
169
+ name: 'collabTrackLastOrganicChangePlugin',
170
+ plugin: createLastOrganicChangePlugin
171
+ });
172
+ }
173
+ return plugins;
162
174
  },
163
175
  onEditorViewStateUpdated: function onEditorViewStateUpdated(props) {
164
176
  var _api$analytics, _api$editorViewMode, _options$useNativePlu;
@@ -4,13 +4,9 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  import { TELEPOINTER_DIM_CLASS } from '@atlaskit/editor-common/collab';
5
5
  import { browser } from '@atlaskit/editor-common/utils';
6
6
  import { Selection } from '@atlaskit/editor-prosemirror/state';
7
- import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
8
7
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
9
8
  import { Participants } from '../../participants';
10
- import { createTelepointers, findPointers, getPositionOfTelepointer } from '../../utils';
11
- var isReplaceStep = function isReplaceStep(step) {
12
- return step instanceof ReplaceStep;
13
- };
9
+ import { createTelepointers, findPointers, getPositionOfTelepointer, isReplaceStep } from '../../utils';
14
10
  export { TELEPOINTER_DIM_CLASS };
15
11
 
16
12
  /**
@@ -0,0 +1,33 @@
1
+ import { isDirtyTransaction } from '@atlaskit/editor-common/collab';
2
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
+ import { isOrganicChange, originalTransactionHasMeta } from '../utils';
5
+ export var trackLastOrganicChangePluginKey = new PluginKey('collabTrackLastOrganicChangePlugin');
6
+ export var createPlugin = function createPlugin() {
7
+ return new SafePlugin({
8
+ key: trackLastOrganicChangePluginKey,
9
+ state: {
10
+ init: function init() {
11
+ return {
12
+ lastOrganicChangeAt: null
13
+ };
14
+ },
15
+ apply: function apply(transaction, prevPluginState) {
16
+ var isRemote = originalTransactionHasMeta(transaction, 'isRemote');
17
+ var isDocumentReplaceFromRemote = isRemote && originalTransactionHasMeta(transaction, 'replaceDocument');
18
+ if (isDocumentReplaceFromRemote) {
19
+ return prevPluginState;
20
+ }
21
+ if (isRemote || isDirtyTransaction(transaction)) {
22
+ return prevPluginState;
23
+ }
24
+ if (isOrganicChange(transaction)) {
25
+ return {
26
+ lastOrganicChangeAt: Date.now()
27
+ };
28
+ }
29
+ return prevPluginState;
30
+ }
31
+ }
32
+ });
33
+ };
@@ -1,20 +1,10 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { isDirtyTransaction } from '@atlaskit/editor-common/collab';
3
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
- import { PluginKey, Transaction } from '@atlaskit/editor-prosemirror/state';
4
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
5
5
  import { AddMarkStep } from '@atlaskit/editor-prosemirror/transform';
6
+ import { originalTransactionHasMeta } from '../utils';
6
7
  export var trackNCSInitializationPluginKey = new PluginKey('collabTrackNCSInitializationPlugin');
7
- var originalTransactionHasMeta = function originalTransactionHasMeta(transaction, metaTag) {
8
- var hasMetaTag = Boolean(transaction.getMeta(metaTag));
9
- if (hasMetaTag) {
10
- return true;
11
- }
12
- var appendedTransaction = transaction.getMeta('appendedTransaction');
13
- if (appendedTransaction instanceof Transaction) {
14
- return originalTransactionHasMeta(appendedTransaction, metaTag);
15
- }
16
- return false;
17
- };
18
8
  export var createPlugin = function createPlugin() {
19
9
  return new SafePlugin({
20
10
  key: trackNCSInitializationPluginKey,
package/dist/esm/utils.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import _typeof from "@babel/runtime/helpers/typeof";
2
+ import { AnalyticsStep, SetAttrsStep } from '@atlaskit/adf-schema/steps';
2
3
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
3
4
  import { ZERO_WIDTH_JOINER } from '@atlaskit/editor-common/whitespace';
5
+ import { Transaction } from '@atlaskit/editor-prosemirror/state';
4
6
  import { Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
7
+ import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
5
8
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
6
9
  import { avatarColors } from '@atlaskit/editor-shared-styles/consts';
7
10
  export var findPointers = function findPointers(id, decorations) {
@@ -131,4 +134,61 @@ export var getPositionOfTelepointer = function getPositionOfTelepointer(sessionI
131
134
  }
132
135
  });
133
136
  return scrollPosition;
137
+ };
138
+ export var isReplaceStep = function isReplaceStep(step) {
139
+ return step instanceof ReplaceStep;
140
+ };
141
+ export var originalTransactionHasMeta = function originalTransactionHasMeta(transaction, metaTag) {
142
+ var hasMetaTag = Boolean(transaction.getMeta(metaTag));
143
+ if (hasMetaTag) {
144
+ return true;
145
+ }
146
+ var appendedTransaction = transaction.getMeta('appendedTransaction');
147
+ if (appendedTransaction instanceof Transaction) {
148
+ return originalTransactionHasMeta(appendedTransaction, metaTag);
149
+ }
150
+ return false;
151
+ };
152
+
153
+ /**
154
+ * This list contains step attributes that do not result from a user action.
155
+ * All steps that contain ONLY the blocked attribute are considered automated steps
156
+ * and should not be recognised as organic change.
157
+ *
158
+ * `attr_colwidth` is an exception to above explanation. Resizing the column
159
+ * currently creates too many steps and is therefore also on this list.
160
+ *
161
+ * Steps analycs dashboard: https://atlassian-discover.cloud.databricks.com/dashboardsv3/01ef4d3c8aa916c8b0cb5332a9f37caf/published?o=4482001201517624
162
+ */
163
+ var blockedAttrsList = ['__contextId', 'localId', '__autoSize', 'attr_colwidth'];
164
+
165
+ /**
166
+ * Takes the transaction and editor state and checks if the transaction is considered organic change
167
+ * @param tr Transaction
168
+ * @returns boolean
169
+ */
170
+ export var isOrganicChange = function isOrganicChange(tr) {
171
+ // If document has not been marked as `docChanged` by PM, skip the rest of the logic
172
+ if (!tr.docChanged) {
173
+ return false;
174
+ }
175
+ return tr.steps.some(function (step) {
176
+ // If a step is an instance of AnalyticsStep, it is not considered organic
177
+ if (step instanceof AnalyticsStep) {
178
+ return false;
179
+ }
180
+ // If a step is not an instance of SetAttrsStep, it is considered organic
181
+ if (!(step instanceof SetAttrsStep)) {
182
+ return true;
183
+ }
184
+ var allAttributes = Object.keys(step.attrs);
185
+ // If a step is an instance of SetAttrsStep, it checks if the attributes in the step
186
+ // are not in the `blockedAttributes`. If one of the attributes not on the list, it considers the change
187
+ // organic but only if the entire document is not equal to the previous state.
188
+ return allAttributes.some(function (attr) {
189
+ if (!blockedAttrsList.includes(attr)) {
190
+ return true;
191
+ }
192
+ }) && !tr.doc.eq(tr.before);
193
+ });
134
194
  };
@@ -0,0 +1,5 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
3
+ import type { LastOrganicChangeMetadata } from '../types';
4
+ export declare const trackLastOrganicChangePluginKey: PluginKey<LastOrganicChangeMetadata>;
5
+ export declare const createPlugin: () => SafePlugin<LastOrganicChangeMetadata>;
@@ -22,6 +22,9 @@ export type CollabInitializedMetadata = {
22
22
  firstChangeAfterInitAt: null | number;
23
23
  firstContentBodyChangeAfterInitAt: null | number;
24
24
  };
25
+ export type LastOrganicChangeMetadata = {
26
+ lastOrganicChangeAt: null | number;
27
+ };
25
28
  export type CollabEditPluginSharedState = {
26
29
  initialised: CollabInitializedMetadata;
27
30
  activeParticipants: ReadOnlyParticipants | undefined;
@@ -1,6 +1,7 @@
1
1
  import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
2
2
  import type { CollabEditOptions, CollabParticipant } from '@atlaskit/editor-common/collab';
3
- import type { EditorState } from '@atlaskit/editor-prosemirror/state';
3
+ import { type EditorState, type ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
4
+ import type { Step } from '@atlaskit/editor-prosemirror/transform';
4
5
  import type { DecorationSet, EditorView } from '@atlaskit/editor-prosemirror/view';
5
6
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
6
7
  export declare const findPointers: (id: string, decorations: DecorationSet) => Decoration[];
@@ -9,6 +10,14 @@ export declare function getAvatarColor(str: string): {
9
10
  color: "var(--ds-icon-accent-lime)" | "var(--ds-icon-accent-red)" | "var(--ds-icon-accent-orange)" | "var(--ds-icon-accent-yellow)" | "var(--ds-icon-accent-green)" | "var(--ds-icon-accent-teal)" | "var(--ds-icon-accent-blue)" | "var(--ds-icon-accent-purple)" | "var(--ds-icon-accent-magenta)" | "var(--ds-icon-accent-gray)" | "var(--ds-background-accent-lime-bolder-hovered)" | "var(--ds-background-accent-red-bolder-hovered)" | "var(--ds-background-accent-orange-bolder-hovered)" | "var(--ds-background-accent-yellow-bolder-hovered)" | "var(--ds-background-accent-yellow-bolder-pressed)" | "var(--ds-background-accent-green-bolder-hovered)" | "var(--ds-background-accent-teal-bolder-hovered)" | "var(--ds-background-accent-blue-bolder-hovered)" | "var(--ds-background-accent-purple-bolder-hovered)" | "var(--ds-background-accent-magenta-bolder-hovered)" | "var(--ds-background-accent-gray-bolder-hovered)";
10
11
  };
11
12
  export declare const createTelepointers: (from: number, to: number, sessionId: string, isSelection: boolean, initial: string) => Decoration[];
12
- export declare const replaceDocument: (doc: any, state: EditorState, version?: number, options?: CollabEditOptions, reserveCursor?: boolean) => import("prosemirror-state").Transaction;
13
+ export declare const replaceDocument: (doc: any, state: EditorState, version?: number, options?: CollabEditOptions, reserveCursor?: boolean) => Transaction;
13
14
  export declare const scrollToCollabCursor: (editorView: EditorView, participants: CollabParticipant[], sessionId: string | undefined, index: number, editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => void;
14
15
  export declare const getPositionOfTelepointer: (sessionId: string, decorationSet: DecorationSet) => undefined | number;
16
+ export declare const isReplaceStep: (step: Step) => boolean;
17
+ export declare const originalTransactionHasMeta: (transaction: Transaction | ReadonlyTransaction, metaTag: string) => boolean;
18
+ /**
19
+ * Takes the transaction and editor state and checks if the transaction is considered organic change
20
+ * @param tr Transaction
21
+ * @returns boolean
22
+ */
23
+ export declare const isOrganicChange: (tr: ReadonlyTransaction) => boolean;
@@ -0,0 +1,5 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
3
+ import type { LastOrganicChangeMetadata } from '../types';
4
+ export declare const trackLastOrganicChangePluginKey: PluginKey<LastOrganicChangeMetadata>;
5
+ export declare const createPlugin: () => SafePlugin<LastOrganicChangeMetadata>;
@@ -22,6 +22,9 @@ export type CollabInitializedMetadata = {
22
22
  firstChangeAfterInitAt: null | number;
23
23
  firstContentBodyChangeAfterInitAt: null | number;
24
24
  };
25
+ export type LastOrganicChangeMetadata = {
26
+ lastOrganicChangeAt: null | number;
27
+ };
25
28
  export type CollabEditPluginSharedState = {
26
29
  initialised: CollabInitializedMetadata;
27
30
  activeParticipants: ReadOnlyParticipants | undefined;
@@ -1,6 +1,7 @@
1
1
  import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
2
2
  import type { CollabEditOptions, CollabParticipant } from '@atlaskit/editor-common/collab';
3
- import type { EditorState } from '@atlaskit/editor-prosemirror/state';
3
+ import { type EditorState, type ReadonlyTransaction, Transaction } from '@atlaskit/editor-prosemirror/state';
4
+ import type { Step } from '@atlaskit/editor-prosemirror/transform';
4
5
  import type { DecorationSet, EditorView } from '@atlaskit/editor-prosemirror/view';
5
6
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
6
7
  export declare const findPointers: (id: string, decorations: DecorationSet) => Decoration[];
@@ -9,6 +10,14 @@ export declare function getAvatarColor(str: string): {
9
10
  color: "var(--ds-icon-accent-lime)" | "var(--ds-icon-accent-red)" | "var(--ds-icon-accent-orange)" | "var(--ds-icon-accent-yellow)" | "var(--ds-icon-accent-green)" | "var(--ds-icon-accent-teal)" | "var(--ds-icon-accent-blue)" | "var(--ds-icon-accent-purple)" | "var(--ds-icon-accent-magenta)" | "var(--ds-icon-accent-gray)" | "var(--ds-background-accent-lime-bolder-hovered)" | "var(--ds-background-accent-red-bolder-hovered)" | "var(--ds-background-accent-orange-bolder-hovered)" | "var(--ds-background-accent-yellow-bolder-hovered)" | "var(--ds-background-accent-yellow-bolder-pressed)" | "var(--ds-background-accent-green-bolder-hovered)" | "var(--ds-background-accent-teal-bolder-hovered)" | "var(--ds-background-accent-blue-bolder-hovered)" | "var(--ds-background-accent-purple-bolder-hovered)" | "var(--ds-background-accent-magenta-bolder-hovered)" | "var(--ds-background-accent-gray-bolder-hovered)";
10
11
  };
11
12
  export declare const createTelepointers: (from: number, to: number, sessionId: string, isSelection: boolean, initial: string) => Decoration[];
12
- export declare const replaceDocument: (doc: any, state: EditorState, version?: number, options?: CollabEditOptions, reserveCursor?: boolean) => import("prosemirror-state").Transaction;
13
+ export declare const replaceDocument: (doc: any, state: EditorState, version?: number, options?: CollabEditOptions, reserveCursor?: boolean) => Transaction;
13
14
  export declare const scrollToCollabCursor: (editorView: EditorView, participants: CollabParticipant[], sessionId: string | undefined, index: number, editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => void;
14
15
  export declare const getPositionOfTelepointer: (sessionId: string, decorationSet: DecorationSet) => undefined | number;
16
+ export declare const isReplaceStep: (step: Step) => boolean;
17
+ export declare const originalTransactionHasMeta: (transaction: Transaction | ReadonlyTransaction, metaTag: string) => boolean;
18
+ /**
19
+ * Takes the transaction and editor state and checks if the transaction is considered organic change
20
+ * @param tr Transaction
21
+ * @returns boolean
22
+ */
23
+ export declare const isOrganicChange: (tr: ReadonlyTransaction) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-collab-edit",
3
- "version": "1.11.2",
3
+ "version": "1.13.0",
4
4
  "description": "Collab Edit plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -32,16 +32,16 @@
32
32
  ".": "./src/index.ts"
33
33
  },
34
34
  "dependencies": {
35
- "@atlaskit/adf-schema": "^40.3.0",
36
- "@atlaskit/custom-steps": "^0.6.0",
37
- "@atlaskit/editor-common": "^87.4.0",
38
- "@atlaskit/editor-plugin-analytics": "1.6.2",
35
+ "@atlaskit/adf-schema": "^40.8.1",
36
+ "@atlaskit/custom-steps": "^0.7.0",
37
+ "@atlaskit/editor-common": "^87.6.0",
38
+ "@atlaskit/editor-plugin-analytics": "1.7.0",
39
39
  "@atlaskit/editor-plugin-editor-viewmode": "^2.1.0",
40
40
  "@atlaskit/editor-plugin-feature-flags": "^1.2.0",
41
41
  "@atlaskit/editor-prosemirror": "5.0.1",
42
42
  "@atlaskit/editor-shared-styles": "^2.13.0",
43
43
  "@atlaskit/platform-feature-flags": "^0.3.0",
44
- "@atlaskit/prosemirror-collab": "^0.8.0",
44
+ "@atlaskit/prosemirror-collab": "^0.9.0",
45
45
  "@babel/runtime": "^7.0.0",
46
46
  "memoize-one": "^6.0.0"
47
47
  },
@@ -51,11 +51,11 @@
51
51
  "devDependencies": {
52
52
  "@af/integration-testing": "*",
53
53
  "@af/visual-regression": "*",
54
- "@atlaskit/editor-plugin-mentions": "^2.2.0",
55
- "@atlaskit/editor-plugin-text-formatting": "^1.12.0",
56
- "@atlaskit/editor-plugin-type-ahead": "^1.6.0",
57
- "@atlaskit/editor-plugin-unsupported-content": "^1.7.0",
58
- "@atlaskit/editor-test-helpers": "^18.29.0",
54
+ "@atlaskit/editor-plugin-mentions": "^2.3.0",
55
+ "@atlaskit/editor-plugin-text-formatting": "^1.13.0",
56
+ "@atlaskit/editor-plugin-type-ahead": "^1.7.0",
57
+ "@atlaskit/editor-plugin-unsupported-content": "^1.8.0",
58
+ "@atlaskit/editor-test-helpers": "^18.30.0",
59
59
  "@atlaskit/ssr": "*",
60
60
  "@atlaskit/synchrony-test-helpers": "^2.5.0",
61
61
  "@atlaskit/util-data-test": "^17.9.0",
@@ -102,6 +102,9 @@
102
102
  "platform-feature-flags": {
103
103
  "platform.editor.no-telecursors-for-viewmode-users_hok8o": {
104
104
  "type": "boolean"
105
+ },
106
+ "platform_editor_last_organic_change": {
107
+ "type": "boolean"
105
108
  }
106
109
  }
107
110
  }