@atlaskit/editor-plugin-collab-edit 0.1.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE.md +13 -0
  3. package/README.md +30 -0
  4. package/dist/cjs/actions.js +110 -0
  5. package/dist/cjs/analytics.js +47 -0
  6. package/dist/cjs/events/handlers.js +88 -0
  7. package/dist/cjs/events/initialize.js +58 -0
  8. package/dist/cjs/events/send-transaction.js +48 -0
  9. package/dist/cjs/index.js +128 -0
  10. package/dist/cjs/native-collab-provider-plugin.js +37 -0
  11. package/dist/cjs/participants.js +95 -0
  12. package/dist/cjs/plugin-key.js +8 -0
  13. package/dist/cjs/plugin-state.js +241 -0
  14. package/dist/cjs/plugin.js +102 -0
  15. package/dist/cjs/types.js +5 -0
  16. package/dist/cjs/utils.js +150 -0
  17. package/dist/es2019/actions.js +119 -0
  18. package/dist/es2019/analytics.js +41 -0
  19. package/dist/es2019/events/handlers.js +72 -0
  20. package/dist/es2019/events/initialize.js +44 -0
  21. package/dist/es2019/events/send-transaction.js +42 -0
  22. package/dist/es2019/index.js +86 -0
  23. package/dist/es2019/native-collab-provider-plugin.js +32 -0
  24. package/dist/es2019/participants.js +57 -0
  25. package/dist/es2019/plugin-key.js +2 -0
  26. package/dist/es2019/plugin-state.js +219 -0
  27. package/dist/es2019/plugin.js +83 -0
  28. package/dist/es2019/types.js +1 -0
  29. package/dist/es2019/utils.js +133 -0
  30. package/dist/esm/actions.js +103 -0
  31. package/dist/esm/analytics.js +41 -0
  32. package/dist/esm/events/handlers.js +82 -0
  33. package/dist/esm/events/initialize.js +51 -0
  34. package/dist/esm/events/send-transaction.js +42 -0
  35. package/dist/esm/index.js +116 -0
  36. package/dist/esm/native-collab-provider-plugin.js +31 -0
  37. package/dist/esm/participants.js +88 -0
  38. package/dist/esm/plugin-key.js +2 -0
  39. package/dist/esm/plugin-state.js +229 -0
  40. package/dist/esm/plugin.js +85 -0
  41. package/dist/esm/types.js +1 -0
  42. package/dist/esm/utils.js +136 -0
  43. package/dist/types/actions.d.ts +11 -0
  44. package/dist/types/analytics.d.ts +6 -0
  45. package/dist/types/events/handlers.d.ts +24 -0
  46. package/dist/types/events/initialize.d.ts +16 -0
  47. package/dist/types/events/send-transaction.d.ts +11 -0
  48. package/dist/types/index.d.ts +29 -0
  49. package/dist/types/native-collab-provider-plugin.d.ts +7 -0
  50. package/dist/types/participants.d.ts +18 -0
  51. package/dist/types/plugin-key.d.ts +3 -0
  52. package/dist/types/plugin-state.d.ts +25 -0
  53. package/dist/types/plugin.d.ts +11 -0
  54. package/dist/types/types.d.ts +8 -0
  55. package/dist/types/utils.d.ts +16 -0
  56. package/dist/types-ts4.5/actions.d.ts +11 -0
  57. package/dist/types-ts4.5/analytics.d.ts +6 -0
  58. package/dist/types-ts4.5/events/handlers.d.ts +24 -0
  59. package/dist/types-ts4.5/events/initialize.d.ts +16 -0
  60. package/dist/types-ts4.5/events/send-transaction.d.ts +11 -0
  61. package/dist/types-ts4.5/index.d.ts +29 -0
  62. package/dist/types-ts4.5/native-collab-provider-plugin.d.ts +7 -0
  63. package/dist/types-ts4.5/participants.d.ts +18 -0
  64. package/dist/types-ts4.5/plugin-key.d.ts +3 -0
  65. package/dist/types-ts4.5/plugin-state.d.ts +25 -0
  66. package/dist/types-ts4.5/plugin.d.ts +11 -0
  67. package/dist/types-ts4.5/types.d.ts +8 -0
  68. package/dist/types-ts4.5/utils.d.ts +16 -0
  69. package/package.json +104 -0
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.PluginState = void 0;
8
+ Object.defineProperty(exports, "TELEPOINTER_DIM_CLASS", {
9
+ enumerable: true,
10
+ get: function get() {
11
+ return _collab.TELEPOINTER_DIM_CLASS;
12
+ }
13
+ });
14
+ exports.getValidPos = void 0;
15
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
16
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
17
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
18
+ var _collab = require("@atlaskit/editor-common/collab");
19
+ var _utils = require("@atlaskit/editor-common/utils");
20
+ var _state = require("@atlaskit/editor-prosemirror/state");
21
+ var _transform = require("@atlaskit/editor-prosemirror/transform");
22
+ var _view = require("@atlaskit/editor-prosemirror/view");
23
+ var _participants = require("./participants");
24
+ var _utils2 = require("./utils");
25
+ var isReplaceStep = function isReplaceStep(step) {
26
+ return step instanceof _transform.ReplaceStep;
27
+ };
28
+ /**
29
+ * Returns position where it's possible to place a decoration.
30
+ */
31
+ var getValidPos = exports.getValidPos = function getValidPos(tr, pos) {
32
+ var endOfDocPos = tr.doc.nodeSize - 2;
33
+ if (pos <= endOfDocPos) {
34
+ var resolvedPos = tr.doc.resolve(pos);
35
+ var backwardSelection = _state.Selection.findFrom(resolvedPos, -1, true);
36
+ // if there's no correct cursor position before the `pos`, we try to find it after the `pos`
37
+ var forwardSelection = _state.Selection.findFrom(resolvedPos, 1, true);
38
+ return backwardSelection ? backwardSelection.from : forwardSelection ? forwardSelection.from : pos;
39
+ }
40
+ return endOfDocPos;
41
+ };
42
+ var PluginState = exports.PluginState = /*#__PURE__*/function () {
43
+ function PluginState(decorations, participants, sessionId) {
44
+ var collabInitalised = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
45
+ var onError = arguments.length > 4 ? arguments[4] : undefined;
46
+ (0, _classCallCheck2.default)(this, PluginState);
47
+ // eslint-disable-next-line no-console
48
+ (0, _defineProperty2.default)(this, "onError", function (error) {
49
+ return console.error(error);
50
+ });
51
+ this.decorationSet = decorations;
52
+ this.participants = participants;
53
+ this.sid = sessionId;
54
+ this.isReady = collabInitalised;
55
+ this.onError = onError || this.onError;
56
+ }
57
+ (0, _createClass2.default)(PluginState, [{
58
+ key: "decorations",
59
+ get: function get() {
60
+ return this.decorationSet;
61
+ }
62
+ }, {
63
+ key: "activeParticipants",
64
+ get: function get() {
65
+ return this.participants;
66
+ }
67
+ }, {
68
+ key: "sessionId",
69
+ get: function get() {
70
+ return this.sid;
71
+ }
72
+ }, {
73
+ key: "getInitial",
74
+ value: function getInitial(sessionId) {
75
+ var participant = this.participants.get(sessionId);
76
+ return participant ? participant.name.substring(0, 1).toUpperCase() : 'X';
77
+ }
78
+ }, {
79
+ key: "apply",
80
+ value: function apply(tr) {
81
+ var _this = this;
82
+ var participants = this.participants,
83
+ sid = this.sid,
84
+ isReady = this.isReady;
85
+ var presenceData = tr.getMeta('presence');
86
+ var telepointerData = tr.getMeta('telepointer');
87
+ var sessionIdData = tr.getMeta('sessionId');
88
+ var collabInitialised = tr.getMeta('collabInitialised');
89
+ if (typeof collabInitialised !== 'boolean') {
90
+ collabInitialised = isReady;
91
+ }
92
+ if (sessionIdData) {
93
+ sid = sessionIdData.sid;
94
+ }
95
+ var add = [];
96
+ var remove = [];
97
+ if (presenceData) {
98
+ var _presenceData$joined = presenceData.joined,
99
+ joined = _presenceData$joined === void 0 ? [] : _presenceData$joined,
100
+ _presenceData$left = presenceData.left,
101
+ left = _presenceData$left === void 0 ? [] : _presenceData$left;
102
+ participants = participants.remove(left.map(function (i) {
103
+ return i.sessionId;
104
+ }));
105
+ participants = participants.add(joined);
106
+
107
+ // Remove telepointers for users that left
108
+ left.forEach(function (i) {
109
+ var pointers = (0, _utils2.findPointers)(i.sessionId, _this.decorationSet);
110
+ if (pointers) {
111
+ remove = remove.concat(pointers);
112
+ }
113
+ });
114
+ }
115
+ if (telepointerData) {
116
+ var sessionId = telepointerData.sessionId;
117
+ if (participants.get(sessionId) && sessionId !== sid) {
118
+ var oldPointers = (0, _utils2.findPointers)(telepointerData.sessionId, this.decorationSet);
119
+ if (oldPointers) {
120
+ remove = remove.concat(oldPointers);
121
+ }
122
+ var endOfDocPos = tr.doc.nodeSize - 2;
123
+ var anchor = telepointerData.selection.anchor;
124
+ var head = telepointerData.selection.head;
125
+ var rawFrom = anchor < head ? anchor : head;
126
+ var rawTo = anchor >= head ? anchor : head;
127
+ if (rawFrom > endOfDocPos) {
128
+ rawFrom = endOfDocPos;
129
+ }
130
+ if (rawTo > endOfDocPos) {
131
+ rawTo = endOfDocPos;
132
+ }
133
+ var isSelection = rawTo - rawFrom > 0;
134
+ var from = 1;
135
+ var to = 1;
136
+ try {
137
+ from = getValidPos(tr, isSelection ? Math.max(rawFrom - 1, 0) : rawFrom);
138
+ to = isSelection ? getValidPos(tr, rawTo) : from;
139
+ } catch (err) {
140
+ this.onError(err);
141
+ }
142
+ add = add.concat((0, _utils2.createTelepointers)(from, to, sessionId, isSelection, this.getInitial(sessionId)));
143
+ }
144
+ }
145
+ if (tr.docChanged) {
146
+ // Adjust decoration positions to changes made by the transaction
147
+ try {
148
+ this.decorationSet = this.decorationSet.map(tr.mapping, tr.doc, {
149
+ // Reapplies decorators those got removed by the state change
150
+ onRemove: function onRemove(spec) {
151
+ if (spec.pointer && spec.pointer.sessionId && spec.key === "telepointer-".concat(spec.pointer.sessionId)) {
152
+ var step = tr.steps.filter(isReplaceStep)[0];
153
+ if (step) {
154
+ var _sessionId = spec.pointer.sessionId;
155
+ var _ref = step,
156
+ size = _ref.slice.content.size,
157
+ _from = _ref.from;
158
+ var pos = getValidPos(tr, size ? Math.min(_from + size, tr.doc.nodeSize - 3) : Math.max(_from, 1));
159
+ add = add.concat((0, _utils2.createTelepointers)(pos, pos, _sessionId, false, _this.getInitial(_sessionId)));
160
+ }
161
+ }
162
+ }
163
+ });
164
+ } catch (err) {
165
+ this.onError(err);
166
+ }
167
+
168
+ // Remove any selection decoration within the change range,
169
+ // takes care of the issue when after pasting we end up with a dead selection
170
+ tr.steps.filter(isReplaceStep).forEach(function (s) {
171
+ var _ref2 = s,
172
+ from = _ref2.from,
173
+ to = _ref2.to;
174
+ _this.decorationSet.find(from, to).forEach(function (deco) {
175
+ // `type` is private, `from` and `to` are public in latest version
176
+ // `from` != `to` means it's a selection
177
+ if (deco.from !== deco.to) {
178
+ remove.push(deco);
179
+ }
180
+ });
181
+ });
182
+ }
183
+ var selection = tr.selection;
184
+ this.decorationSet.find().forEach(function (deco) {
185
+ if (deco.type.toDOM) {
186
+ var hasTelepointerDimClass = deco.type.toDOM.classList.contains(_collab.TELEPOINTER_DIM_CLASS);
187
+ if (deco.from === selection.from && deco.to === selection.to) {
188
+ if (!hasTelepointerDimClass) {
189
+ deco.type.toDOM.classList.add(_collab.TELEPOINTER_DIM_CLASS);
190
+ }
191
+
192
+ // Browser condition here to fix ED-14722 where telepointer
193
+ // decorations with side -1 in Firefox causes backspace issues.
194
+ // This is likely caused by contenteditable quirks in Firefox
195
+ if (!_utils.browser.gecko) {
196
+ deco.type.side = -1;
197
+ }
198
+ } else {
199
+ if (hasTelepointerDimClass) {
200
+ deco.type.toDOM.classList.remove(_collab.TELEPOINTER_DIM_CLASS);
201
+ }
202
+ deco.type.side = 0;
203
+ }
204
+ }
205
+ });
206
+ if (remove.length) {
207
+ this.decorationSet = this.decorationSet.remove(remove);
208
+ }
209
+ if (add.length) {
210
+ this.decorationSet = this.decorationSet.add(tr.doc, add);
211
+ }
212
+
213
+ // This piece needs to be after the decorationSet adjustments,
214
+ // otherwise it's always one step behind where the cursor is
215
+ if (telepointerData) {
216
+ var _sessionId2 = telepointerData.sessionId;
217
+ if (participants.get(_sessionId2)) {
218
+ var positionForScroll = (0, _utils2.getPositionOfTelepointer)(_sessionId2, this.decorationSet);
219
+ if (positionForScroll) {
220
+ participants = participants.updateCursorPos(_sessionId2, positionForScroll);
221
+ }
222
+ }
223
+ }
224
+ var nextState = new PluginState(this.decorationSet, participants, sid, collabInitialised);
225
+ return PluginState.eq(nextState, this) ? this : nextState;
226
+ }
227
+ }], [{
228
+ key: "eq",
229
+ value: function eq(a, b) {
230
+ return a.participants === b.participants && a.sessionId === b.sessionId && a.isReady === b.isReady;
231
+ }
232
+ }, {
233
+ key: "init",
234
+ value: function init(config) {
235
+ var doc = config.doc,
236
+ onError = config.onError;
237
+ return new PluginState(_view.DecorationSet.create(doc, []), new _participants.Participants(), undefined, undefined, onError);
238
+ }
239
+ }]);
240
+ return PluginState;
241
+ }();
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "PluginState", {
7
+ enumerable: true,
8
+ get: function get() {
9
+ return _pluginState.PluginState;
10
+ }
11
+ });
12
+ exports.createPlugin = void 0;
13
+ Object.defineProperty(exports, "pluginKey", {
14
+ enumerable: true,
15
+ get: function get() {
16
+ return _pluginKey.pluginKey;
17
+ }
18
+ });
19
+ var _analytics = require("@atlaskit/editor-common/analytics");
20
+ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
21
+ var _analytics2 = require("./analytics");
22
+ var _initialize = require("./events/initialize");
23
+ var _pluginKey = require("./plugin-key");
24
+ var _pluginState = require("./plugin-state");
25
+ var createPlugin = exports.createPlugin = function createPlugin(dispatch, providerFactory, providerResolver, collabProviderCallback, options, featureFlags, pluginInjectionApi) {
26
+ return new _safePlugin.SafePlugin({
27
+ key: _pluginKey.pluginKey,
28
+ state: {
29
+ init: function init(config) {
30
+ return _pluginState.PluginState.init(config);
31
+ },
32
+ apply: function apply(transaction, prevPluginState, _oldEditorState, _newEditorState) {
33
+ var pluginState = prevPluginState.apply(transaction);
34
+ dispatch(_pluginKey.pluginKey, pluginState);
35
+ return pluginState;
36
+ }
37
+ },
38
+ props: {
39
+ decorations: function decorations(state) {
40
+ var _pluginKey$getState;
41
+ return (_pluginKey$getState = _pluginKey.pluginKey.getState(state)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.decorations;
42
+ }
43
+ },
44
+ filterTransaction: function filterTransaction(tr, state) {
45
+ var pluginState = _pluginKey.pluginKey.getState(state);
46
+ var collabInitialiseTr = tr.getMeta('collabInitialised');
47
+
48
+ // Don't allow transactions that modifies the document before
49
+ // collab-plugin is ready.
50
+ if (collabInitialiseTr) {
51
+ return true;
52
+ }
53
+ if (!(pluginState !== null && pluginState !== void 0 && pluginState.isReady) && tr.docChanged) {
54
+ return false;
55
+ }
56
+ return true;
57
+ },
58
+ view: function view(_view) {
59
+ var _pluginInjectionApi$a, _pluginInjectionApi$a4;
60
+ var addErrorAnalytics = (0, _analytics2.addSynchronyErrorAnalytics)(_view.state, _view.state.tr, featureFlags, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions);
61
+ var onSyncUpError = function onSyncUpError(attributes) {
62
+ var _pluginInjectionApi$a2, _pluginInjectionApi$a3;
63
+ var fireAnalyticsCallback = (0, _analytics.fireAnalyticsEvent)((_pluginInjectionApi$a2 = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a3 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a3 === void 0 || (_pluginInjectionApi$a3 = _pluginInjectionApi$a3.sharedState.currentState()) === null || _pluginInjectionApi$a3 === void 0 ? void 0 : _pluginInjectionApi$a3.createAnalyticsEvent) !== null && _pluginInjectionApi$a2 !== void 0 ? _pluginInjectionApi$a2 : undefined);
64
+ fireAnalyticsCallback({
65
+ payload: {
66
+ action: _analytics.ACTION.NEW_COLLAB_SYNC_UP_ERROR_NO_STEPS,
67
+ actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
68
+ eventType: _analytics.EVENT_TYPE.OPERATIONAL,
69
+ attributes: attributes
70
+ }
71
+ });
72
+ };
73
+ options.onSyncUpError = onSyncUpError;
74
+ var cleanup = collabProviderCallback((0, _initialize.initialize)({
75
+ view: _view,
76
+ options: options,
77
+ providerFactory: providerFactory,
78
+ featureFlags: featureFlags,
79
+ editorAnalyticsApi: pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a4 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a4 === void 0 ? void 0 : _pluginInjectionApi$a4.actions
80
+ }), addErrorAnalytics);
81
+ providerFactory && providerFactory.subscribe('collabEditProvider', function (_name, providerPromise) {
82
+ if (providerPromise) {
83
+ providerPromise.then(function (provider) {
84
+ return providerResolver(provider);
85
+ });
86
+ }
87
+ });
88
+ return {
89
+ destroy: function destroy() {
90
+ providerFactory.unsubscribeAll('collabEditProvider');
91
+ if (cleanup) {
92
+ cleanup.then(function (unsubscribe) {
93
+ if (unsubscribe) {
94
+ unsubscribe();
95
+ }
96
+ });
97
+ }
98
+ }
99
+ };
100
+ }
101
+ });
102
+ };
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ Object.defineProperty(exports, "colors", {
8
+ enumerable: true,
9
+ get: function get() {
10
+ return _collab.colors;
11
+ }
12
+ });
13
+ exports.findPointers = exports.createTelepointers = void 0;
14
+ exports.getAvatarColor = getAvatarColor;
15
+ exports.scrollToCollabCursor = exports.replaceDocument = exports.getPositionOfTelepointer = void 0;
16
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
17
+ var _analytics = require("@atlaskit/editor-common/analytics");
18
+ var _collab = require("@atlaskit/editor-common/collab");
19
+ var _utils = require("@atlaskit/editor-common/utils");
20
+ var _state = require("@atlaskit/editor-prosemirror/state");
21
+ var _view = require("@atlaskit/editor-prosemirror/view");
22
+ var findPointers = exports.findPointers = function findPointers(id, decorations) {
23
+ return decorations.find().reduce(function (arr, deco) {
24
+ return deco.spec.pointer.sessionId === id ? arr.concat(deco) : arr;
25
+ }, []);
26
+ };
27
+ function style(options) {
28
+ var color = options && options.color || 'black';
29
+ return "border-left: 1px solid ".concat(color, "; border-right: 1px solid ").concat(color, "; margin-right: -2px;");
30
+ }
31
+ function getAvatarColor(str) {
32
+ var hash = 0;
33
+ for (var i = 0; i < str.length; i++) {
34
+ /* eslint-disable no-bitwise */
35
+ hash = (hash << 5) - hash + str.charCodeAt(i);
36
+ hash = hash & hash;
37
+ /* eslint-enable no-bitwise */
38
+ }
39
+
40
+ var index = Math.abs(hash) % _collab.colors.length;
41
+ return {
42
+ index: index,
43
+ color: _collab.colors[index]
44
+ };
45
+ }
46
+ var createTelepointers = exports.createTelepointers = function createTelepointers(from, to, sessionId, isSelection, initial) {
47
+ var decorations = [];
48
+ var avatarColor = getAvatarColor(sessionId);
49
+ var color = avatarColor.index.toString();
50
+ if (isSelection) {
51
+ var className = "telepointer color-".concat(color, " telepointer-selection");
52
+ decorations.push(_view.Decoration.inline(from, to, {
53
+ class: className,
54
+ 'data-initial': initial
55
+ }, {
56
+ pointer: {
57
+ sessionId: sessionId
58
+ }
59
+ }));
60
+ }
61
+ var spaceJoinerBefore = document.createElement('span');
62
+ spaceJoinerBefore.textContent = _utils.ZERO_WIDTH_JOINER;
63
+ var spaceJoinerAfter = document.createElement('span');
64
+ spaceJoinerAfter.textContent = _utils.ZERO_WIDTH_JOINER;
65
+ var cursor = document.createElement('span');
66
+ cursor.textContent = _utils.ZERO_WIDTH_JOINER;
67
+ cursor.className = "telepointer color-".concat(color, " telepointer-selection-badge");
68
+ cursor.style.cssText = "".concat(style({
69
+ color: avatarColor.color.solid
70
+ }), ";");
71
+ cursor.setAttribute('data-initial', initial);
72
+ return decorations.concat(_view.Decoration.widget(to, spaceJoinerAfter, {
73
+ pointer: {
74
+ sessionId: sessionId
75
+ },
76
+ key: "telepointer-".concat(sessionId, "-zero")
77
+ })).concat(_view.Decoration.widget(to, cursor, {
78
+ pointer: {
79
+ sessionId: sessionId
80
+ },
81
+ key: "telepointer-".concat(sessionId)
82
+ })).concat(_view.Decoration.widget(to, spaceJoinerBefore, {
83
+ pointer: {
84
+ sessionId: sessionId
85
+ },
86
+ key: "telepointer-".concat(sessionId, "-zero")
87
+ }));
88
+ };
89
+ var replaceDocument = exports.replaceDocument = function replaceDocument(doc, state, version, options, reserveCursor) {
90
+ var schema = state.schema,
91
+ tr = state.tr;
92
+ var content = (doc.content || []).map(function (child) {
93
+ return schema.nodeFromJSON(child);
94
+ });
95
+ var hasContent = !!content.length;
96
+ if (hasContent) {
97
+ tr.setMeta('addToHistory', false);
98
+ tr.replaceWith(0, state.doc.nodeSize - 2, content);
99
+ var selection = state.selection;
100
+ if (reserveCursor) {
101
+ // If the cursor is still in the range of the new document,
102
+ // keep where it was.
103
+ if (selection.to < tr.doc.content.size - 2) {
104
+ var $from = tr.doc.resolve(selection.from);
105
+ var $to = tr.doc.resolve(selection.to);
106
+ var newselection = new _state.TextSelection($from, $to);
107
+ tr.setSelection(newselection);
108
+ }
109
+ } else {
110
+ tr.setSelection(_state.Selection.atStart(tr.doc));
111
+ }
112
+ tr.setMeta('replaceDocument', true);
113
+ if ((0, _typeof2.default)(version) !== undefined && options && options.useNativePlugin) {
114
+ var collabState = {
115
+ version: version,
116
+ unconfirmed: []
117
+ };
118
+ tr.setMeta('collab$', collabState);
119
+ }
120
+ }
121
+ return tr;
122
+ };
123
+ var scrollToCollabCursor = exports.scrollToCollabCursor = function scrollToCollabCursor(editorView, participants, sessionId, index, editorAnalyticsAPI) {
124
+ var selectedUser = participants[index];
125
+ if (selectedUser && selectedUser.cursorPos !== undefined && selectedUser.sessionId !== sessionId) {
126
+ var state = editorView.state;
127
+ var tr = state.tr;
128
+ var analyticsPayload = {
129
+ action: _analytics.ACTION.MATCHED,
130
+ actionSubject: _analytics.ACTION_SUBJECT.SELECTION,
131
+ eventType: _analytics.EVENT_TYPE.TRACK
132
+ };
133
+ tr.setSelection(_state.Selection.near(tr.doc.resolve(selectedUser.cursorPos)));
134
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(analyticsPayload)(tr);
135
+ tr.scrollIntoView();
136
+ editorView.dispatch(tr);
137
+ if (!editorView.hasFocus()) {
138
+ editorView.focus();
139
+ }
140
+ }
141
+ };
142
+ var getPositionOfTelepointer = exports.getPositionOfTelepointer = function getPositionOfTelepointer(sessionId, decorationSet) {
143
+ var scrollPosition;
144
+ decorationSet.find().forEach(function (deco) {
145
+ if (deco.type.spec.pointer.sessionId === sessionId) {
146
+ scrollPosition = deco.from;
147
+ }
148
+ });
149
+ return scrollPosition;
150
+ };
@@ -0,0 +1,119 @@
1
+ import { receiveTransaction } from 'prosemirror-collab';
2
+ import { AllSelection, NodeSelection } from '@atlaskit/editor-prosemirror/state';
3
+ import { Step } from '@atlaskit/editor-prosemirror/transform';
4
+ import { replaceDocument } from './utils';
5
+ export const handleInit = (initData, view, options) => {
6
+ const {
7
+ doc,
8
+ json,
9
+ version,
10
+ reserveCursor
11
+ } = initData;
12
+ if (doc) {
13
+ const {
14
+ state
15
+ } = view;
16
+ const tr = replaceDocument(doc, state, version, options, reserveCursor);
17
+ tr.setMeta('isRemote', true);
18
+ view.dispatch(tr);
19
+ } else if (json) {
20
+ applyRemoteSteps(json, view);
21
+ }
22
+ };
23
+ export const handleConnection = (connectionData, view) => {
24
+ const {
25
+ state: {
26
+ tr
27
+ }
28
+ } = view;
29
+ view.dispatch(tr.setMeta('sessionId', connectionData));
30
+ };
31
+ export const handlePresence = (presenceData, view) => {
32
+ const {
33
+ state: {
34
+ tr
35
+ }
36
+ } = view;
37
+ view.dispatch(tr.setMeta('presence', presenceData));
38
+ };
39
+ export const applyRemoteData = (remoteData, view, options) => {
40
+ const {
41
+ json,
42
+ userIds = []
43
+ } = remoteData;
44
+ if (json) {
45
+ applyRemoteSteps(json, view, userIds, options);
46
+ }
47
+ };
48
+ export const applyRemoteSteps = (json, view, userIds, options) => {
49
+ if (!json || !json.length) {
50
+ return;
51
+ }
52
+ const {
53
+ state,
54
+ state: {
55
+ schema
56
+ }
57
+ } = view;
58
+ const steps = json.map(step => Step.fromJSON(schema, step));
59
+ let tr;
60
+ if (options && options.useNativePlugin && userIds) {
61
+ tr = receiveTransaction(state, steps, userIds);
62
+ } else {
63
+ tr = state.tr;
64
+ steps.forEach(step => tr.step(step));
65
+ }
66
+ if (tr) {
67
+ tr.setMeta('addToHistory', false);
68
+ tr.setMeta('isRemote', true);
69
+ const {
70
+ from,
71
+ to
72
+ } = state.selection;
73
+ const [firstStep] = json;
74
+
75
+ /*
76
+ * Persist marks across transactions. Fixes an issue where
77
+ * marks are lost if remote transactions are dispatched
78
+ * between a user creating the mark and typing.
79
+ */
80
+ if (state.tr.storedMarks) {
81
+ tr.setStoredMarks(state.tr.storedMarks);
82
+ }
83
+
84
+ /**
85
+ * If the cursor is a the same position as the first step in
86
+ * the remote data, we need to manually set it back again
87
+ * in order to prevent the cursor from moving.
88
+ */
89
+ if (from === firstStep.from && to === firstStep.to) {
90
+ tr.setSelection(state.selection.map(tr.doc, tr.mapping));
91
+ }
92
+ view.dispatch(tr);
93
+ }
94
+ };
95
+ export const handleTelePointer = (telepointerData, view) => {
96
+ const {
97
+ state: {
98
+ tr
99
+ }
100
+ } = view;
101
+ view.dispatch(tr.setMeta('telepointer', telepointerData));
102
+ };
103
+ function isAllSelection(selection) {
104
+ return selection instanceof AllSelection;
105
+ }
106
+ function isNodeSelection(selection) {
107
+ return selection instanceof NodeSelection;
108
+ }
109
+ export const getSendableSelection = selection => {
110
+ /**
111
+ * <kbd>CMD + A</kbd> triggers a AllSelection
112
+ * <kbd>escape</kbd> triggers a NodeSelection
113
+ */
114
+ return {
115
+ type: 'textSelection',
116
+ anchor: selection.anchor,
117
+ head: isAllSelection(selection) || isNodeSelection(selection) ? selection.head - 1 : selection.head
118
+ };
119
+ };
@@ -0,0 +1,41 @@
1
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
+ import { getDocStructure } from '@atlaskit/editor-common/core-utils';
3
+ import { sniffUserBrowserExtensions } from '@atlaskit/editor-common/utils';
4
+ export const addSynchronyErrorAnalytics = (state, tr, featureFlags, editorAnalyticsApi) => {
5
+ return error => {
6
+ const browserExtensions = sniffUserBrowserExtensions({
7
+ extensions: ['grammarly']
8
+ });
9
+ const payload = {
10
+ action: ACTION.SYNCHRONY_ERROR,
11
+ actionSubject: ACTION_SUBJECT.EDITOR,
12
+ eventType: EVENT_TYPE.OPERATIONAL,
13
+ attributes: {
14
+ error,
15
+ browserExtensions
16
+ }
17
+ };
18
+ if (featureFlags.synchronyErrorDocStructure) {
19
+ payload.attributes.docStructure = getDocStructure(state.doc, {
20
+ compact: true
21
+ });
22
+ }
23
+ editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent(payload)(tr);
24
+ return tr;
25
+ };
26
+ };
27
+ export const addSynchronyEntityAnalytics = (state, tr) => {
28
+ return (type, editorAnalyticsApi) => {
29
+ editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent({
30
+ action: type === 'error' ? ACTION.SYNCHRONY_ENTITY_ERROR : ACTION.SYNCHRONY_DISCONNECTED,
31
+ actionSubject: ACTION_SUBJECT.EDITOR,
32
+ eventType: EVENT_TYPE.OPERATIONAL,
33
+ attributes: {
34
+ // https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine
35
+ onLine: navigator.onLine,
36
+ visibilityState: document.visibilityState
37
+ }
38
+ })(tr);
39
+ return tr;
40
+ };
41
+ };