@atlaskit/editor-plugin-mentions 18.2.9 → 18.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @atlaskit/editor-plugin-mentions
2
2
 
3
+ ## 18.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`5a0128690a065`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5a0128690a065) -
8
+ Add mention avatars to Rovo editor and renderer surfaces behind
9
+ platform_editor_rovo_editor_mention_node_avatar
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 18.2.10
16
+
17
+ ### Patch Changes
18
+
19
+ - [`0927c3666c010`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0927c3666c010) -
20
+ Upgrade `uuid` from `3.x` to `11.1.1` to remediate GHSA-w5hq-g745-h8pq / SNYK-JS-UUID-16133035.
21
+
22
+ `uuid@11` removed the deep subpath exports (`uuid/v4`, `uuid/v1`, `uuid/v5`) and the default
23
+ export, so all internal call sites were migrated to named imports:
24
+
25
+ ```diff
26
+ -import uuid from 'uuid/v4';
27
+ +import { v4 as uuid } from 'uuid';
28
+
29
+ -import uuid from 'uuid';
30
+ +import { v4 as uuid } from 'uuid';
31
+ ```
32
+
33
+ With the exception of `@atlassian/integrations` (below), this is an internal implementation change
34
+ only - no public API, export, or entrypoint changed. UUID generation behaviour is unchanged
35
+ (`uuid@3`'s default export was already `v4`).
36
+
37
+ `@atlassian/integrations` declares `uuid` as a peer dependency, so its declared range moved from
38
+ `^3.1.0` to `^11.1.1`. That is a peer dependency declaration change, hence `minor` rather than
39
+ `patch` for that package.
40
+
41
+ The following `platform/packages/ai-mate` packages were also touched, but are all `private: true`
42
+ and so are intentionally not listed in the frontmatter above:
43
+ - `@atlassian/csm-assistance-service` - bumped its explicit `uuid` dependency from `npm:^9.0.0` to
44
+ `npm:^11.1.1` (`9.0.1` is also within the advisory's affected range).
45
+ - `@atlassian/csm-guidance-config` - example helper only, migrated to the named `uuid` import.
46
+ - `@atlassian/csm-ui-components` - example helper only, migrated to the named `uuid` import.
47
+
48
+ - Updated dependencies
49
+
3
50
  ## 18.2.9
4
51
 
5
52
  ### Patch Changes
@@ -1,11 +1,10 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
3
  Object.defineProperty(exports, "__esModule", {
5
4
  value: true
6
5
  });
7
6
  exports.insertMention = exports.createSingleMentionFragment = void 0;
8
- var _uuid = _interopRequireDefault(require("uuid"));
7
+ var _uuid = require("uuid");
9
8
  var _utils = require("@atlaskit/editor-common/utils");
10
9
  var _model = require("@atlaskit/editor-prosemirror/model");
11
10
  var _resource = require("@atlaskit/mention/resource");
@@ -49,7 +48,7 @@ var createSingleMentionFragment = exports.createSingleMentionFragment = function
49
48
  accessLevel: accessLevel,
50
49
  userType: userType === 'DEFAULT' ? null : userType,
51
50
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
52
- localId: localId !== null && localId !== void 0 ? localId : (0, _uuid.default)()
51
+ localId: localId !== null && localId !== void 0 ? localId : (0, _uuid.v4)()
53
52
  }, null, annotationMarksForPos);
54
53
  var space = schema.text(' ', annotationMarksForPos);
55
54
  return _model.Fragment.from([mentionNode, space]);
@@ -12,7 +12,7 @@ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"))
12
12
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
13
  var _react = _interopRequireWildcard(require("react"));
14
14
  var _reactIntl = require("react-intl");
15
- var _uuid = _interopRequireDefault(require("uuid"));
15
+ var _uuid = require("uuid");
16
16
  var _analytics = require("@atlaskit/editor-common/analytics");
17
17
  var _messages = require("@atlaskit/editor-common/messages");
18
18
  var _providerFactory = require("@atlaskit/editor-common/provider-factory");
@@ -91,7 +91,7 @@ var mentionsPlugin = exports.mentionsPlugin = function mentionsPlugin(_ref3) {
91
91
  var options = _ref3.config,
92
92
  api = _ref3.api;
93
93
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
94
- var sessionId = (0, _uuid.default)();
94
+ var sessionId = (0, _uuid.v4)();
95
95
  var previousMediaProvider;
96
96
  var fireEvent = function fireEvent(payload, channel) {
97
97
  var _api$analytics;
@@ -12,7 +12,7 @@ var _runtime = require("@compiled/react/runtime");
12
12
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
13
  var _react = _interopRequireWildcard(require("react"));
14
14
  var _bindEventListener = require("bind-event-listener");
15
- var _v = _interopRequireDefault(require("uuid/v4"));
15
+ var _uuid = require("uuid");
16
16
  var _Tooltip = _interopRequireDefault(require("@atlaskit/tooltip/Tooltip"));
17
17
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
18
18
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- mirror existing renderer pattern
@@ -155,7 +155,7 @@ var disabledTooltipRenderer = exports.disabledTooltipRenderer = function disable
155
155
  var chipElement = _ref5.chipElement,
156
156
  portalProviderAPI = _ref5.portalProviderAPI;
157
157
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- mirror existing renderer pattern
158
- var key = (0, _v.default)();
158
+ var key = (0, _uuid.v4)();
159
159
  var currentTooltip;
160
160
  var listeners = new Set();
161
161
  var broadcast = function broadcast() {
@@ -8,7 +8,7 @@ exports.profileCardRenderer = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _react = _interopRequireDefault(require("react"));
10
10
  var _bindEventListener = require("bind-event-listener");
11
- var _v = _interopRequireDefault(require("uuid/v4"));
11
+ var _uuid = require("uuid");
12
12
  var _state = require("@atlaskit/editor-prosemirror/state");
13
13
  var _utils = require("@atlaskit/editor-prosemirror/utils");
14
14
  var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
@@ -32,7 +32,7 @@ var profileCardRenderer = exports.profileCardRenderer = function profileCardRend
32
32
  var renderingProfileCard = false;
33
33
  var navigatingToProfile = false;
34
34
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
35
- var key = (0, _v.default)();
35
+ var key = (0, _uuid.v4)();
36
36
  var cleanupSelection;
37
37
  var removeProfileCard = function removeProfileCard() {
38
38
  var _cleanupSelection;
@@ -43,7 +43,7 @@ var ACTIONS = exports.ACTIONS = {
43
43
  CLEAR_PENDING_PASTED_AGENT_MENTION: 'CLEAR_PENDING_PASTED_AGENT_MENTION'
44
44
  };
45
45
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
46
- var PACKAGE_VERSION = "18.2.8";
46
+ var PACKAGE_VERSION = "18.2.10";
47
47
  var setProvider = function setProvider(provider) {
48
48
  return function (state, dispatch) {
49
49
  if (dispatch) {
@@ -42,6 +42,20 @@ var toMentionNode = function toMentionNode(_ref) {
42
42
  var isMentionNode = function isMentionNode(node) {
43
43
  return node.type === 'mention' && node.attrs !== undefined && 'id' in node.attrs && typeof node.attrs.id === 'string';
44
44
  };
45
+ var SUBSCRIPTION_KEY_PREFIX = 'mention-node-data-provider';
46
+ var subscriptionKeyCounter = 0;
47
+ var cacheMentionData = function cacheMentionData(cache, mentions) {
48
+ mentions.forEach(function (mention) {
49
+ if (mention.isPlaceholder || !mention.avatarUrl) {
50
+ cache.delete(mention.id);
51
+ return;
52
+ }
53
+ cache.set(mention.id, {
54
+ avatarUrl: mention.avatarUrl,
55
+ appType: mention.appType
56
+ });
57
+ });
58
+ };
45
59
 
46
60
  /**
47
61
  * Page-scoped data provider for mention avatars.
@@ -59,6 +73,9 @@ var isMentionNode = function isMentionNode(node) {
59
73
  * invoked. A Relay adapter can query `users(accountIds:)`, then map each user's
60
74
  * `picture` and app type to a `MentionNodeData` record keyed by account ID. The
61
75
  * resolved result is cached by the underlying `NodeDataProvider`.
76
+ *
77
+ * Products that already have a `MentionProvider` result stream can call `connect`
78
+ * so emitted mention results populate a synchronous avatar cache.
62
79
  */
63
80
  var MentionNodeDataProvider = exports.MentionNodeDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
64
81
  function MentionNodeDataProvider(fetchMentionNodeData) {
@@ -67,6 +84,11 @@ var MentionNodeDataProvider = exports.MentionNodeDataProvider = /*#__PURE__*/fun
67
84
  (0, _classCallCheck2.default)(this, MentionNodeDataProvider);
68
85
  _this = _callSuper(this, MentionNodeDataProvider);
69
86
  (0, _defineProperty2.default)(_this, "name", 'mentionNodeDataProvider');
87
+ (0, _defineProperty2.default)(_this, "abortControllerRef", {
88
+ current: new AbortController()
89
+ });
90
+ (0, _defineProperty2.default)(_this, "emittedMentionCache", new Map());
91
+ (0, _defineProperty2.default)(_this, "subscriptionKey", "".concat(SUBSCRIPTION_KEY_PREFIX, ":").concat(++subscriptionKeyCounter));
70
92
  (0, _defineProperty2.default)(_this, "queuedFetches", []);
71
93
  (0, _defineProperty2.default)(_this, "isFlushScheduled", false);
72
94
  _this.fetchMentionNodeData = fetchMentionNodeData;
@@ -118,8 +140,7 @@ var MentionNodeDataProvider = exports.MentionNodeDataProvider = /*#__PURE__*/fun
118
140
  }, {
119
141
  key: "getMentionData",
120
142
  value: function getMentionData(mention, callback) {
121
- var _this$resolveMentionN;
122
- var resolvedData = (_this$resolveMentionN = this.resolveMentionNodeData) === null || _this$resolveMentionN === void 0 ? void 0 : _this$resolveMentionN.call(this, mention);
143
+ var resolvedData = this.resolveMentionNodeDataFromCache(mention);
123
144
  if (resolvedData) {
124
145
  callback({
125
146
  data: enrichMentionNodeData(mention, resolvedData)
@@ -131,20 +152,59 @@ var MentionNodeDataProvider = exports.MentionNodeDataProvider = /*#__PURE__*/fun
131
152
  }, {
132
153
  key: "getMentionDataFromCache",
133
154
  value: function getMentionDataFromCache(mention) {
134
- var _this$resolveMentionN2, _this$getNodeDataFrom;
135
- var resolvedData = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention);
155
+ var _this$getNodeDataFrom;
156
+ var resolvedData = this.resolveMentionNodeDataFromCache(mention);
136
157
  if (resolvedData) {
137
158
  return enrichMentionNodeData(mention, resolvedData);
138
159
  }
139
160
  var data = (_this$getNodeDataFrom = this.getNodeDataFromCache(toMentionNode(mention))) === null || _this$getNodeDataFrom === void 0 ? void 0 : _this$getNodeDataFrom.data;
140
161
  return data ? enrichMentionNodeData(mention, data) : undefined;
141
162
  }
163
+ }, {
164
+ key: "connect",
165
+ value: function connect(mentionResource) {
166
+ var _this2 = this;
167
+ if (this.lastMentionResource && this.lastMentionResource !== mentionResource) {
168
+ this.emittedMentionCache.clear();
169
+ this.resetCache();
170
+ }
171
+ this.lastMentionResource = mentionResource;
172
+ if (this.abortControllerRef.current.signal.aborted) {
173
+ this.abortControllerRef.current = new AbortController();
174
+ }
175
+ var isDisconnected = false;
176
+ var subscribedMentionProvider;
177
+ void mentionResource.then(function (mentionProvider) {
178
+ if (isDisconnected) {
179
+ return;
180
+ }
181
+ subscribedMentionProvider = mentionProvider;
182
+ mentionProvider.subscribe(_this2.subscriptionKey, function (mentions) {
183
+ cacheMentionData(_this2.emittedMentionCache, mentions);
184
+ });
185
+ }).catch(function () {
186
+ return undefined;
187
+ });
188
+ return function () {
189
+ var _subscribedMentionPro;
190
+ isDisconnected = true;
191
+ _this2.abortControllerRef.current.abort();
192
+ (_subscribedMentionPro = subscribedMentionProvider) === null || _subscribedMentionPro === void 0 || _subscribedMentionPro.unsubscribe(_this2.subscriptionKey);
193
+ subscribedMentionProvider = undefined;
194
+ };
195
+ }
196
+ }, {
197
+ key: "resolveMentionNodeDataFromCache",
198
+ value: function resolveMentionNodeDataFromCache(mention) {
199
+ var _this$resolveMentionN, _this$resolveMentionN2;
200
+ return (_this$resolveMentionN = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention)) !== null && _this$resolveMentionN !== void 0 ? _this$resolveMentionN : this.emittedMentionCache.get(mention.id);
201
+ }
142
202
  }, {
143
203
  key: "enqueueFetch",
144
204
  value: function enqueueFetch(accountIds) {
145
- var _this2 = this;
205
+ var _this3 = this;
146
206
  var result = new Promise(function (resolve, reject) {
147
- _this2.queuedFetches.push({
207
+ _this3.queuedFetches.push({
148
208
  accountIds: accountIds,
149
209
  reject: reject,
150
210
  resolve: resolve
@@ -153,7 +213,7 @@ var MentionNodeDataProvider = exports.MentionNodeDataProvider = /*#__PURE__*/fun
153
213
  if (!this.isFlushScheduled) {
154
214
  this.isFlushScheduled = true;
155
215
  void Promise.resolve().then(function () {
156
- return _this2.flushFetches();
216
+ return _this3.flushFetches();
157
217
  });
158
218
  }
159
219
  return result;
@@ -174,7 +234,7 @@ var MentionNodeDataProvider = exports.MentionNodeDataProvider = /*#__PURE__*/fun
174
234
  })));
175
235
  _context2.prev = 1;
176
236
  _context2.next = 2;
177
- return this.fetchMentionNodeData(accountIds);
237
+ return this.fetchMentionNodeData(accountIds, this.abortControllerRef.current.signal);
178
238
  case 2:
179
239
  data = _context2.sent;
180
240
  queuedFetches.forEach(function (_ref4) {
@@ -11,7 +11,7 @@ exports.mentionToTypeaheadItem = void 0;
11
11
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
12
12
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
13
13
  var _react = _interopRequireDefault(require("react"));
14
- var _uuid = _interopRequireDefault(require("uuid"));
14
+ var _uuid = require("uuid");
15
15
  var _messages = require("@atlaskit/editor-common/messages");
16
16
  var _typeAhead = require("@atlaskit/editor-common/type-ahead");
17
17
  var _model = require("@atlaskit/editor-prosemirror/model");
@@ -283,7 +283,7 @@ var buildNodesForTeamMention = function buildNodesForTeamMention(schema, selecte
283
283
  accessLevel: accessLevel,
284
284
  userType: 'DEFAULT',
285
285
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
286
- localId: (0, _uuid.default)()
286
+ localId: (0, _uuid.v4)()
287
287
  });
288
288
  inlineNodes.push(userMentionNode);
289
289
  // should not add empty space after the last user mention.
@@ -378,7 +378,7 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
378
378
  showAgentMentionsLabsLozenge = _ref0$showAgentMentio === void 0 ? false : _ref0$showAgentMentio,
379
379
  profilecardProvider = _ref0.profilecardProvider;
380
380
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
381
- var sessionId = (0, _uuid.default)();
381
+ var sessionId = (0, _uuid.v4)();
382
382
  var firstQueryWithoutResults = null;
383
383
  var lastTypeAheadQuery = '';
384
384
  var subscriptionKeys = new Set();
@@ -423,7 +423,7 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
423
423
  contextIdentifierProvider = _ref10.contextIdentifierProvider;
424
424
  return new Promise(function (resolve, reject) {
425
425
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
426
- var key = "loadingMentionsForTypeAhead_".concat((0, _uuid.default)());
426
+ var key = "loadingMentionsForTypeAhead_".concat((0, _uuid.v4)());
427
427
  var mentionsSubscribeCallback = function mentionsSubscribeCallback(mentions) {
428
428
  var resultQuery = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
429
429
  var stats = arguments.length > 2 ? arguments[2] : undefined;
@@ -589,7 +589,7 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
589
589
  }
590
590
 
591
591
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
592
- var mentionLocalId = (0, _uuid.default)();
592
+ var mentionLocalId = (0, _uuid.v4)();
593
593
  if (handleMentionsChanged) {
594
594
  var mentionChange = _objectSpread({
595
595
  id: id,
@@ -612,7 +612,7 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
612
612
  }), query, contextIdentifierProvider, taskListId, taskItemId, isAgentMentionSelection, enableAgentSectioning));
613
613
 
614
614
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
615
- sessionId = (0, _uuid.default)();
615
+ sessionId = (0, _uuid.v4)();
616
616
  if (mentionProvider && (0, _utils3.isTeamType)(userType)) {
617
617
  return insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
618
618
  }
@@ -679,7 +679,7 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
679
679
  subscriptionKeys.clear();
680
680
 
681
681
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
682
- sessionId = (0, _uuid.default)();
682
+ sessionId = (0, _uuid.v4)();
683
683
  }
684
684
  };
685
685
 
@@ -717,7 +717,7 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
717
717
  contextIdentifierProvider = _ref16.contextIdentifierProvider;
718
718
 
719
719
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
720
- var key = "loadingMentionsForTypeAhead_".concat((0, _uuid.default)());
720
+ var key = "loadingMentionsForTypeAhead_".concat((0, _uuid.v4)());
721
721
  var initialResolve = null;
722
722
  var initialReject = null;
723
723
  var initialResolved = false;
@@ -1,5 +1,5 @@
1
1
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
2
- import uuid from 'uuid';
2
+ import { v4 as uuid } from 'uuid';
3
3
  import { getAnnotationMarksForPos } from '@atlaskit/editor-common/utils';
4
4
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
5
5
  import { isResolvingMentionProvider } from '@atlaskit/mention/resource';
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
4
- import uuid from 'uuid';
4
+ import { v4 as uuid } from 'uuid';
5
5
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
6
6
  import { toolbarInsertBlockMessages as messages, mentionMessages } from '@atlaskit/editor-common/messages';
7
7
  import { WithProviders } from '@atlaskit/editor-common/provider-factory';
@@ -4,7 +4,7 @@ import { ax, ix } from "@compiled/react/runtime";
4
4
  import React, { useEffect, useState } from 'react';
5
5
  import { bindAll } from 'bind-event-listener';
6
6
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- mirror existing renderer pattern
7
- import uuid from 'uuid/v4';
7
+ import { v4 as uuid } from 'uuid';
8
8
  import Tooltip from '@atlaskit/tooltip/Tooltip';
9
9
  const styles = {
10
10
  hiddenTrigger: "_1e0cglyw"
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { bind } from 'bind-event-listener';
3
3
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
4
- import uuid from 'uuid/v4';
4
+ import { v4 as uuid } from 'uuid';
5
5
  import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
6
6
  import { findChildrenByAttr } from '@atlaskit/editor-prosemirror/utils';
7
7
  import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
@@ -32,7 +32,7 @@ export const ACTIONS = {
32
32
  CLEAR_PENDING_PASTED_AGENT_MENTION: 'CLEAR_PENDING_PASTED_AGENT_MENTION'
33
33
  };
34
34
  const PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
35
- const PACKAGE_VERSION = "18.2.8";
35
+ const PACKAGE_VERSION = "18.2.10";
36
36
  const setProvider = provider => (state, dispatch) => {
37
37
  if (dispatch) {
38
38
  dispatch(state.tr.setMeta(mentionPluginKey, {
@@ -20,6 +20,20 @@ const toMentionNode = ({
20
20
  type: 'mention'
21
21
  });
22
22
  const isMentionNode = node => node.type === 'mention' && node.attrs !== undefined && 'id' in node.attrs && typeof node.attrs.id === 'string';
23
+ const SUBSCRIPTION_KEY_PREFIX = 'mention-node-data-provider';
24
+ let subscriptionKeyCounter = 0;
25
+ const cacheMentionData = (cache, mentions) => {
26
+ mentions.forEach(mention => {
27
+ if (mention.isPlaceholder || !mention.avatarUrl) {
28
+ cache.delete(mention.id);
29
+ return;
30
+ }
31
+ cache.set(mention.id, {
32
+ avatarUrl: mention.avatarUrl,
33
+ appType: mention.appType
34
+ });
35
+ });
36
+ };
23
37
 
24
38
  /**
25
39
  * Page-scoped data provider for mention avatars.
@@ -37,11 +51,19 @@ const isMentionNode = node => node.type === 'mention' && node.attrs !== undefine
37
51
  * invoked. A Relay adapter can query `users(accountIds:)`, then map each user's
38
52
  * `picture` and app type to a `MentionNodeData` record keyed by account ID. The
39
53
  * resolved result is cached by the underlying `NodeDataProvider`.
54
+ *
55
+ * Products that already have a `MentionProvider` result stream can call `connect`
56
+ * so emitted mention results populate a synchronous avatar cache.
40
57
  */
41
58
  export class MentionNodeDataProvider extends NodeDataProvider {
42
59
  constructor(fetchMentionNodeData, resolveMentionNodeData = undefined) {
43
60
  super();
44
61
  _defineProperty(this, "name", 'mentionNodeDataProvider');
62
+ _defineProperty(this, "abortControllerRef", {
63
+ current: new AbortController()
64
+ });
65
+ _defineProperty(this, "emittedMentionCache", new Map());
66
+ _defineProperty(this, "subscriptionKey", `${SUBSCRIPTION_KEY_PREFIX}:${++subscriptionKeyCounter}`);
45
67
  _defineProperty(this, "queuedFetches", []);
46
68
  _defineProperty(this, "isFlushScheduled", false);
47
69
  this.fetchMentionNodeData = fetchMentionNodeData;
@@ -63,8 +85,7 @@ export class MentionNodeDataProvider extends NodeDataProvider {
63
85
  }) => enrichMentionNodeData(attrs, dataByAccountId[attrs.id]));
64
86
  }
65
87
  getMentionData(mention, callback) {
66
- var _this$resolveMentionN;
67
- const resolvedData = (_this$resolveMentionN = this.resolveMentionNodeData) === null || _this$resolveMentionN === void 0 ? void 0 : _this$resolveMentionN.call(this, mention);
88
+ const resolvedData = this.resolveMentionNodeDataFromCache(mention);
68
89
  if (resolvedData) {
69
90
  callback({
70
91
  data: enrichMentionNodeData(mention, resolvedData)
@@ -74,14 +95,46 @@ export class MentionNodeDataProvider extends NodeDataProvider {
74
95
  this.getData(toMentionNode(mention), callback);
75
96
  }
76
97
  getMentionDataFromCache(mention) {
77
- var _this$resolveMentionN2, _this$getNodeDataFrom;
78
- const resolvedData = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention);
98
+ var _this$getNodeDataFrom;
99
+ const resolvedData = this.resolveMentionNodeDataFromCache(mention);
79
100
  if (resolvedData) {
80
101
  return enrichMentionNodeData(mention, resolvedData);
81
102
  }
82
103
  const data = (_this$getNodeDataFrom = this.getNodeDataFromCache(toMentionNode(mention))) === null || _this$getNodeDataFrom === void 0 ? void 0 : _this$getNodeDataFrom.data;
83
104
  return data ? enrichMentionNodeData(mention, data) : undefined;
84
105
  }
106
+ connect(mentionResource) {
107
+ if (this.lastMentionResource && this.lastMentionResource !== mentionResource) {
108
+ this.emittedMentionCache.clear();
109
+ this.resetCache();
110
+ }
111
+ this.lastMentionResource = mentionResource;
112
+ if (this.abortControllerRef.current.signal.aborted) {
113
+ this.abortControllerRef.current = new AbortController();
114
+ }
115
+ let isDisconnected = false;
116
+ let subscribedMentionProvider;
117
+ void mentionResource.then(mentionProvider => {
118
+ if (isDisconnected) {
119
+ return;
120
+ }
121
+ subscribedMentionProvider = mentionProvider;
122
+ mentionProvider.subscribe(this.subscriptionKey, mentions => {
123
+ cacheMentionData(this.emittedMentionCache, mentions);
124
+ });
125
+ }).catch(() => undefined);
126
+ return () => {
127
+ var _subscribedMentionPro;
128
+ isDisconnected = true;
129
+ this.abortControllerRef.current.abort();
130
+ (_subscribedMentionPro = subscribedMentionProvider) === null || _subscribedMentionPro === void 0 ? void 0 : _subscribedMentionPro.unsubscribe(this.subscriptionKey);
131
+ subscribedMentionProvider = undefined;
132
+ };
133
+ }
134
+ resolveMentionNodeDataFromCache(mention) {
135
+ var _this$resolveMentionN, _this$resolveMentionN2;
136
+ return (_this$resolveMentionN = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention)) !== null && _this$resolveMentionN !== void 0 ? _this$resolveMentionN : this.emittedMentionCache.get(mention.id);
137
+ }
85
138
  enqueueFetch(accountIds) {
86
139
  const result = new Promise((resolve, reject) => {
87
140
  this.queuedFetches.push({
@@ -102,7 +155,7 @@ export class MentionNodeDataProvider extends NodeDataProvider {
102
155
  this.queuedFetches = [];
103
156
  const accountIds = Array.from(new Set(queuedFetches.flatMap(request => request.accountIds)));
104
157
  try {
105
- const data = await this.fetchMentionNodeData(accountIds);
158
+ const data = await this.fetchMentionNodeData(accountIds, this.abortControllerRef.current.signal);
106
159
  queuedFetches.forEach(({
107
160
  resolve
108
161
  }) => resolve(data));
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
4
- import uuid from 'uuid';
4
+ import { v4 as uuid } from 'uuid';
5
5
  import { mentionMessages } from '@atlaskit/editor-common/messages';
6
6
  import { TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
7
7
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
@@ -1,5 +1,5 @@
1
1
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
2
- import uuid from 'uuid';
2
+ import { v4 as uuid } from 'uuid';
3
3
  import { getAnnotationMarksForPos } from '@atlaskit/editor-common/utils';
4
4
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
5
5
  import { isResolvingMentionProvider } from '@atlaskit/mention/resource';
@@ -8,7 +8,7 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
8
8
  import React, { useEffect, useMemo } from 'react';
9
9
  import { useIntl } from 'react-intl';
10
10
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
11
- import uuid from 'uuid';
11
+ import { v4 as uuid } from 'uuid';
12
12
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
13
13
  import { toolbarInsertBlockMessages as messages, mentionMessages } from '@atlaskit/editor-common/messages';
14
14
  import { WithProviders } from '@atlaskit/editor-common/provider-factory';
@@ -5,7 +5,7 @@ import { ax, ix } from "@compiled/react/runtime";
5
5
  import React, { useEffect, useState } from 'react';
6
6
  import { bindAll } from 'bind-event-listener';
7
7
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- mirror existing renderer pattern
8
- import uuid from 'uuid/v4';
8
+ import { v4 as uuid } from 'uuid';
9
9
  import Tooltip from '@atlaskit/tooltip/Tooltip';
10
10
  var styles = {
11
11
  hiddenTrigger: "_1e0cglyw"
@@ -4,7 +4,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  import React from 'react';
5
5
  import { bind } from 'bind-event-listener';
6
6
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
7
- import uuid from 'uuid/v4';
7
+ import { v4 as uuid } from 'uuid';
8
8
  import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
9
9
  import { findChildrenByAttr } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
@@ -35,7 +35,7 @@ export var ACTIONS = {
35
35
  CLEAR_PENDING_PASTED_AGENT_MENTION: 'CLEAR_PENDING_PASTED_AGENT_MENTION'
36
36
  };
37
37
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
38
- var PACKAGE_VERSION = "18.2.8";
38
+ var PACKAGE_VERSION = "18.2.10";
39
39
  var setProvider = function setProvider(provider) {
40
40
  return function (state, dispatch) {
41
41
  if (dispatch) {
@@ -35,6 +35,20 @@ var toMentionNode = function toMentionNode(_ref) {
35
35
  var isMentionNode = function isMentionNode(node) {
36
36
  return node.type === 'mention' && node.attrs !== undefined && 'id' in node.attrs && typeof node.attrs.id === 'string';
37
37
  };
38
+ var SUBSCRIPTION_KEY_PREFIX = 'mention-node-data-provider';
39
+ var subscriptionKeyCounter = 0;
40
+ var cacheMentionData = function cacheMentionData(cache, mentions) {
41
+ mentions.forEach(function (mention) {
42
+ if (mention.isPlaceholder || !mention.avatarUrl) {
43
+ cache.delete(mention.id);
44
+ return;
45
+ }
46
+ cache.set(mention.id, {
47
+ avatarUrl: mention.avatarUrl,
48
+ appType: mention.appType
49
+ });
50
+ });
51
+ };
38
52
 
39
53
  /**
40
54
  * Page-scoped data provider for mention avatars.
@@ -52,6 +66,9 @@ var isMentionNode = function isMentionNode(node) {
52
66
  * invoked. A Relay adapter can query `users(accountIds:)`, then map each user's
53
67
  * `picture` and app type to a `MentionNodeData` record keyed by account ID. The
54
68
  * resolved result is cached by the underlying `NodeDataProvider`.
69
+ *
70
+ * Products that already have a `MentionProvider` result stream can call `connect`
71
+ * so emitted mention results populate a synchronous avatar cache.
55
72
  */
56
73
  export var MentionNodeDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
57
74
  function MentionNodeDataProvider(fetchMentionNodeData) {
@@ -60,6 +77,11 @@ export var MentionNodeDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
60
77
  _classCallCheck(this, MentionNodeDataProvider);
61
78
  _this = _callSuper(this, MentionNodeDataProvider);
62
79
  _defineProperty(_this, "name", 'mentionNodeDataProvider');
80
+ _defineProperty(_this, "abortControllerRef", {
81
+ current: new AbortController()
82
+ });
83
+ _defineProperty(_this, "emittedMentionCache", new Map());
84
+ _defineProperty(_this, "subscriptionKey", "".concat(SUBSCRIPTION_KEY_PREFIX, ":").concat(++subscriptionKeyCounter));
63
85
  _defineProperty(_this, "queuedFetches", []);
64
86
  _defineProperty(_this, "isFlushScheduled", false);
65
87
  _this.fetchMentionNodeData = fetchMentionNodeData;
@@ -111,8 +133,7 @@ export var MentionNodeDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
111
133
  }, {
112
134
  key: "getMentionData",
113
135
  value: function getMentionData(mention, callback) {
114
- var _this$resolveMentionN;
115
- var resolvedData = (_this$resolveMentionN = this.resolveMentionNodeData) === null || _this$resolveMentionN === void 0 ? void 0 : _this$resolveMentionN.call(this, mention);
136
+ var resolvedData = this.resolveMentionNodeDataFromCache(mention);
116
137
  if (resolvedData) {
117
138
  callback({
118
139
  data: enrichMentionNodeData(mention, resolvedData)
@@ -124,20 +145,59 @@ export var MentionNodeDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
124
145
  }, {
125
146
  key: "getMentionDataFromCache",
126
147
  value: function getMentionDataFromCache(mention) {
127
- var _this$resolveMentionN2, _this$getNodeDataFrom;
128
- var resolvedData = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention);
148
+ var _this$getNodeDataFrom;
149
+ var resolvedData = this.resolveMentionNodeDataFromCache(mention);
129
150
  if (resolvedData) {
130
151
  return enrichMentionNodeData(mention, resolvedData);
131
152
  }
132
153
  var data = (_this$getNodeDataFrom = this.getNodeDataFromCache(toMentionNode(mention))) === null || _this$getNodeDataFrom === void 0 ? void 0 : _this$getNodeDataFrom.data;
133
154
  return data ? enrichMentionNodeData(mention, data) : undefined;
134
155
  }
156
+ }, {
157
+ key: "connect",
158
+ value: function connect(mentionResource) {
159
+ var _this2 = this;
160
+ if (this.lastMentionResource && this.lastMentionResource !== mentionResource) {
161
+ this.emittedMentionCache.clear();
162
+ this.resetCache();
163
+ }
164
+ this.lastMentionResource = mentionResource;
165
+ if (this.abortControllerRef.current.signal.aborted) {
166
+ this.abortControllerRef.current = new AbortController();
167
+ }
168
+ var isDisconnected = false;
169
+ var subscribedMentionProvider;
170
+ void mentionResource.then(function (mentionProvider) {
171
+ if (isDisconnected) {
172
+ return;
173
+ }
174
+ subscribedMentionProvider = mentionProvider;
175
+ mentionProvider.subscribe(_this2.subscriptionKey, function (mentions) {
176
+ cacheMentionData(_this2.emittedMentionCache, mentions);
177
+ });
178
+ }).catch(function () {
179
+ return undefined;
180
+ });
181
+ return function () {
182
+ var _subscribedMentionPro;
183
+ isDisconnected = true;
184
+ _this2.abortControllerRef.current.abort();
185
+ (_subscribedMentionPro = subscribedMentionProvider) === null || _subscribedMentionPro === void 0 || _subscribedMentionPro.unsubscribe(_this2.subscriptionKey);
186
+ subscribedMentionProvider = undefined;
187
+ };
188
+ }
189
+ }, {
190
+ key: "resolveMentionNodeDataFromCache",
191
+ value: function resolveMentionNodeDataFromCache(mention) {
192
+ var _this$resolveMentionN, _this$resolveMentionN2;
193
+ return (_this$resolveMentionN = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention)) !== null && _this$resolveMentionN !== void 0 ? _this$resolveMentionN : this.emittedMentionCache.get(mention.id);
194
+ }
135
195
  }, {
136
196
  key: "enqueueFetch",
137
197
  value: function enqueueFetch(accountIds) {
138
- var _this2 = this;
198
+ var _this3 = this;
139
199
  var result = new Promise(function (resolve, reject) {
140
- _this2.queuedFetches.push({
200
+ _this3.queuedFetches.push({
141
201
  accountIds: accountIds,
142
202
  reject: reject,
143
203
  resolve: resolve
@@ -146,7 +206,7 @@ export var MentionNodeDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
146
206
  if (!this.isFlushScheduled) {
147
207
  this.isFlushScheduled = true;
148
208
  void Promise.resolve().then(function () {
149
- return _this2.flushFetches();
209
+ return _this3.flushFetches();
150
210
  });
151
211
  }
152
212
  return result;
@@ -167,7 +227,7 @@ export var MentionNodeDataProvider = /*#__PURE__*/function (_NodeDataProvider) {
167
227
  })));
168
228
  _context2.prev = 1;
169
229
  _context2.next = 2;
170
- return this.fetchMentionNodeData(accountIds);
230
+ return this.fetchMentionNodeData(accountIds, this.abortControllerRef.current.signal);
171
231
  case 2:
172
232
  data = _context2.sent;
173
233
  queuedFetches.forEach(function (_ref4) {
@@ -8,7 +8,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
8
8
  import React from 'react';
9
9
 
10
10
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
11
- import uuid from 'uuid';
11
+ import { v4 as uuid } from 'uuid';
12
12
  import { mentionMessages } from '@atlaskit/editor-common/messages';
13
13
  import { TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
14
14
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
@@ -1,9 +1,10 @@
1
1
  import type { MentionDefinition } from '@atlaskit/adf-schema/mention';
2
2
  import type { MentionNodeDataCallback, MentionNodeDataIdentifier, MentionNodeDataProvider as MentionNodeDataProviderContract } from '@atlaskit/editor-common/mention';
3
3
  import type { JSONNode } from '@atlaskit/editor-json-transformer/types';
4
+ import type { MentionProvider } from '@atlaskit/mention/resource';
4
5
  import type { MentionNodeData } from '@atlaskit/mention/types';
5
6
  import { NodeDataProvider } from '@atlaskit/node-data-provider/node-data-provider';
6
- export type FetchMentionNodeData = (accountIds: string[]) => Promise<Record<string, MentionNodeData>>;
7
+ export type FetchMentionNodeData = (accountIds: string[], signal?: AbortSignal) => Promise<Record<string, MentionNodeData>>;
7
8
  export type ResolveMentionNodeData = (mention: MentionNodeDataIdentifier) => MentionNodeData | undefined;
8
9
  /**
9
10
  * Page-scoped data provider for mention avatars.
@@ -21,11 +22,18 @@ export type ResolveMentionNodeData = (mention: MentionNodeDataIdentifier) => Men
21
22
  * invoked. A Relay adapter can query `users(accountIds:)`, then map each user's
22
23
  * `picture` and app type to a `MentionNodeData` record keyed by account ID. The
23
24
  * resolved result is cached by the underlying `NodeDataProvider`.
25
+ *
26
+ * Products that already have a `MentionProvider` result stream can call `connect`
27
+ * so emitted mention results populate a synchronous avatar cache.
24
28
  */
25
29
  export declare class MentionNodeDataProvider extends NodeDataProvider<MentionDefinition, MentionNodeData> implements MentionNodeDataProviderContract {
26
30
  private readonly fetchMentionNodeData;
27
31
  private readonly resolveMentionNodeData;
28
32
  readonly name = "mentionNodeDataProvider";
33
+ private readonly abortControllerRef;
34
+ private readonly emittedMentionCache;
35
+ private readonly subscriptionKey;
36
+ private lastMentionResource;
29
37
  private queuedFetches;
30
38
  private isFlushScheduled;
31
39
  constructor(fetchMentionNodeData: FetchMentionNodeData, resolveMentionNodeData?: ResolveMentionNodeData | undefined);
@@ -34,6 +42,8 @@ export declare class MentionNodeDataProvider extends NodeDataProvider<MentionDef
34
42
  fetchNodesData(nodes: MentionDefinition[]): Promise<MentionNodeData[]>;
35
43
  getMentionData(mention: MentionNodeDataIdentifier, callback: MentionNodeDataCallback): void;
36
44
  getMentionDataFromCache(mention: MentionNodeDataIdentifier): MentionNodeData | undefined;
45
+ connect(mentionResource: Promise<MentionProvider>): () => void;
46
+ private resolveMentionNodeDataFromCache;
37
47
  private enqueueFetch;
38
48
  private flushFetches;
39
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-mentions",
3
- "version": "18.2.9",
3
+ "version": "18.3.0",
4
4
  "description": "Mentions plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -56,10 +56,10 @@
56
56
  "bind-event-listener": "^3.0.0",
57
57
  "focus-trap": "^2.4.5",
58
58
  "react-loadable": "^5.1.0",
59
- "uuid": "^3.1.0"
59
+ "uuid": "^11.1.1"
60
60
  },
61
61
  "peerDependencies": {
62
- "@atlaskit/editor-common": "^120.13.0",
62
+ "@atlaskit/editor-common": "^120.14.0",
63
63
  "react": "^18.2.0 || ^19.2.0",
64
64
  "react-dom": "^18.2.0 || ^19.2.0",
65
65
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"