@atlaskit/editor-plugin-mentions 18.2.10 → 18.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @atlaskit/editor-plugin-mentions
2
2
 
3
+ ## 18.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`27143ffe51a5a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/27143ffe51a5a) -
8
+ `@atlaskit/editor-common/vanilla-tooltip` now exports `VANILLA_TOOLTIP_DEFAULT_CLASS`. Compose
9
+ tooltip class names from it rather than repeating the `ak-editor-vanilla-tooltip-default` literal.
10
+
11
+ Behind the `platform_editor_use_vanilla_components` experiment, `VanillaTooltip`:
12
+ - no longer throws `InvalidStateError` when shown again before its open delay has elapsed
13
+ - is `pointer-events: none`, so it cannot swallow pointer events meant for the content beneath —
14
+ pass `pointerEvents` in `styles` to opt out
15
+ - closes its popover before `destroy()` removes it from the document
16
+
17
+ Remaining changes are internal: tooltip consumers adopt the new constant, and the emoji tooltip
18
+ uses the shared class in place of an equivalent inline style object.
19
+
20
+ - Updated dependencies
21
+
22
+ ## 18.3.0
23
+
24
+ ### Minor Changes
25
+
26
+ - [`5a0128690a065`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5a0128690a065) -
27
+ Add mention avatars to Rovo editor and renderer surfaces behind
28
+ platform_editor_rovo_editor_mention_node_avatar
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies
33
+
3
34
  ## 18.2.10
4
35
 
5
36
  ### Patch Changes
@@ -31,12 +31,8 @@ var primitiveClassName = 'editor-mention-primitive';
31
31
  var primitiveWithAvatarClassName = 'editor-mention-primitive-with-avatar';
32
32
  var avatarContainerClassName = 'editor-mention-avatar';
33
33
  var mentionTextClassName = 'editor-mention-text';
34
- /**
35
- * Classes applied to the disabled-reason tooltip. `ak-editor-vanilla-tooltip-default` opts into
36
- * the shared `VanillaTooltip` look, defined as `vanillaTooltipDefaultStyles` in both
37
- * EditorContentContainer stylesheets — keep them in sync when renaming.
38
- */
39
- var disabledTooltipClassNames = 'ak-editor-vanilla-tooltip-default mention-disabled-tooltip';
34
+ /** Classes applied to the disabled-reason tooltip: the shared default look, plus our own hook. */
35
+ var disabledTooltipClassNames = "".concat(_vanillaTooltip.VANILLA_TOOLTIP_DEFAULT_CLASS, " mention-disabled-tooltip");
40
36
  var genericMentionIds = ['HipChat', 'all', 'here'];
41
37
  var unknownMentionText = "@".concat(_constants.UNKNOWN_USER_ID);
42
38
  // eslint-disable-next-line require-unicode-regexp
@@ -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.9";
46
+ var PACKAGE_VERSION = "18.3.0";
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) {
@@ -1,7 +1,7 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { getBrowserInfo } from '@atlaskit/editor-common/browser';
3
3
  import { isSSR } from '@atlaskit/editor-common/core-utils';
4
- import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
4
+ import { VANILLA_TOOLTIP_DEFAULT_CLASS, VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
5
5
  import { ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/whitespace';
6
6
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
7
7
  // oxlint-disable-next-line import/no-duplicates
@@ -20,12 +20,8 @@ const primitiveClassName = 'editor-mention-primitive';
20
20
  const primitiveWithAvatarClassName = 'editor-mention-primitive-with-avatar';
21
21
  const avatarContainerClassName = 'editor-mention-avatar';
22
22
  const mentionTextClassName = 'editor-mention-text';
23
- /**
24
- * Classes applied to the disabled-reason tooltip. `ak-editor-vanilla-tooltip-default` opts into
25
- * the shared `VanillaTooltip` look, defined as `vanillaTooltipDefaultStyles` in both
26
- * EditorContentContainer stylesheets — keep them in sync when renaming.
27
- */
28
- const disabledTooltipClassNames = 'ak-editor-vanilla-tooltip-default mention-disabled-tooltip';
23
+ /** Classes applied to the disabled-reason tooltip: the shared default look, plus our own hook. */
24
+ const disabledTooltipClassNames = `${VANILLA_TOOLTIP_DEFAULT_CLASS} mention-disabled-tooltip`;
29
25
  const genericMentionIds = ['HipChat', 'all', 'here'];
30
26
  const unknownMentionText = `@${UNKNOWN_USER_ID}`;
31
27
  // eslint-disable-next-line require-unicode-regexp
@@ -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.9";
35
+ const PACKAGE_VERSION = "18.3.0";
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));
@@ -7,7 +7,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
7
7
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
8
8
  import { getBrowserInfo } from '@atlaskit/editor-common/browser';
9
9
  import { isSSR } from '@atlaskit/editor-common/core-utils';
10
- import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
10
+ import { VANILLA_TOOLTIP_DEFAULT_CLASS, VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
11
11
  import { ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/whitespace';
12
12
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
13
13
  // oxlint-disable-next-line import/no-duplicates
@@ -26,12 +26,8 @@ var primitiveClassName = 'editor-mention-primitive';
26
26
  var primitiveWithAvatarClassName = 'editor-mention-primitive-with-avatar';
27
27
  var avatarContainerClassName = 'editor-mention-avatar';
28
28
  var mentionTextClassName = 'editor-mention-text';
29
- /**
30
- * Classes applied to the disabled-reason tooltip. `ak-editor-vanilla-tooltip-default` opts into
31
- * the shared `VanillaTooltip` look, defined as `vanillaTooltipDefaultStyles` in both
32
- * EditorContentContainer stylesheets — keep them in sync when renaming.
33
- */
34
- var disabledTooltipClassNames = 'ak-editor-vanilla-tooltip-default mention-disabled-tooltip';
29
+ /** Classes applied to the disabled-reason tooltip: the shared default look, plus our own hook. */
30
+ var disabledTooltipClassNames = "".concat(VANILLA_TOOLTIP_DEFAULT_CLASS, " mention-disabled-tooltip");
35
31
  var genericMentionIds = ['HipChat', 'all', 'here'];
36
32
  var unknownMentionText = "@".concat(UNKNOWN_USER_ID);
37
33
  // eslint-disable-next-line require-unicode-regexp
@@ -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.9";
38
+ var PACKAGE_VERSION = "18.3.0";
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) {
@@ -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.10",
3
+ "version": "18.3.1",
4
4
  "description": "Mentions plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -59,14 +59,14 @@
59
59
  "uuid": "^11.1.1"
60
60
  },
61
61
  "peerDependencies": {
62
- "@atlaskit/editor-common": "^120.13.0",
62
+ "@atlaskit/editor-common": "^120.15.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"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@atlaskit/button": "^25.3.0",
69
- "@atlaskit/editor-core": "^226.4.0",
69
+ "@atlaskit/editor-core": "^226.5.0",
70
70
  "@testing-library/react": "^16.3.0",
71
71
  "react": "^19.2.0",
72
72
  "react-dom": "^19.2.0",