@atlaskit/editor-plugin-mentions 30.0.3 → 31.0.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,24 @@
1
1
  # @atlaskit/editor-plugin-mentions
2
2
 
3
+ ## 31.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`84e89f54232c1`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/84e89f54232c1) -
8
+ Render preloaded Rovo mention identity styling during SSR behind platform_editor_mention_rovo.
9
+ - Updated dependencies
10
+
11
+ ## 31.0.0
12
+
13
+ ### Patch Changes
14
+
15
+ - [`cd47720f33219`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cd47720f33219) -
16
+ Report team mentions as creationMethod 'linkpicker_searchResult' instead of 'unknown' on the link
17
+ created event, and add a linkCreationType attribute ('mention' | 'conventionalSmartLink' | 'link')
18
+ so mentions can be told apart from conventional smart links. The team mention change is behind the
19
+ `platform_editor_team_mention_link_analytics` gate.
20
+ - Updated dependencies
21
+
3
22
  ## 30.0.3
4
23
 
5
24
  ### Patch Changes
@@ -62,6 +62,11 @@ var isAgentMentionUserType = function isAgentMentionUserType(userType) {
62
62
  return userType === 'APP' || userType === 'AGENT';
63
63
  };
64
64
 
65
+ // In SSR `HTMLElement` is not defined, so we need to use duck typing here, follow the same pattern used from the emoji and layout plugins.
66
+ var isHTMLElement = function isHTMLElement(node) {
67
+ return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_mention_rovo') && (0, _isSsr.isSSR)() ? 'innerHTML' in node && 'style' in node && 'classList' in node : node instanceof HTMLElement;
68
+ };
69
+
65
70
  /**
66
71
  * Returns true if the node is an APP/AGENT mention whose stored text is a raw
67
72
  * AAID (e.g. '@712020:uuid') rather than a resolved display name.
@@ -173,6 +178,7 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
173
178
  _this$domElement$quer,
174
179
  _api$mention$sharedSt;
175
180
  (0, _classCallCheck2.default)(this, MentionNodeView);
181
+ // Undefined until the initial styling state has been applied.
176
182
  (0, _defineProperty2.default)(this, "hasAvatarSlot", false);
177
183
  (0, _defineProperty2.default)(this, "isDestroyed", false);
178
184
  (0, _defineProperty2.default)(this, "reportAvatarFailure", function (reason) {
@@ -210,7 +216,7 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
210
216
  if (canReportMissingProvider && isAvatarEnabled) {
211
217
  this.reportAvatarFailure('missing_provider');
212
218
  }
213
- this.domElement = dom instanceof HTMLElement ? dom : undefined;
219
+ this.domElement = isHTMLElement(dom) ? dom : undefined;
214
220
  this.mentionPrimitiveElement = this.domElement ? (_this$domElement$quer = this.domElement.querySelector(".".concat(primitiveClassName))) !== null && _this$domElement$quer !== void 0 ? _this$domElement$quer : undefined : undefined;
215
221
  this.mentionTextElement = this.mentionPrimitiveElement;
216
222
  if (this.hasAvatarSlot) {
@@ -293,51 +299,83 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
293
299
  }
294
300
  }
295
301
  }
302
+ }, {
303
+ key: "isRovoChatFromCache",
304
+ value: function isRovoChatFromCache() {
305
+ var _this$config$options;
306
+ return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_mention_rovo') && ((_this$config$options = this.config.options) === null || _this$config$options === void 0 || (_this$config$options = _this$config$options.mentionNodeDataProvider) === null || _this$config$options === void 0 || (_this$config$options = _this$config$options.getMentionDataFromCache({
307
+ id: this.node.attrs.id,
308
+ userType: this.node.attrs.userType
309
+ })) === null || _this$config$options === void 0 ? void 0 : _this$config$options.isRovoChat) === true;
310
+ }
296
311
  }, {
297
312
  key: "resolveRovoChatIdentity",
298
313
  value: function () {
299
314
  var _resolveRovoChatIdentity = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(mentionProvider) {
300
- var identityAccountId;
315
+ var identityAccountId, _t;
301
316
  return _regenerator.default.wrap(function (_context2) {
302
317
  while (1) switch (_context2.prev = _context2.next) {
303
318
  case 0:
304
- if (!(this.rovoChatIdentityProvider === mentionProvider)) {
319
+ if (!this.isRovoChatFromCache()) {
305
320
  _context2.next = 1;
306
321
  break;
307
322
  }
323
+ this.lastMentionProviderForRovoIdentity = mentionProvider;
324
+ this.setRovoChatStyles(true);
308
325
  return _context2.abrupt("return");
309
326
  case 1:
310
- this.rovoChatIdentityProvider = mentionProvider;
311
- if (!(!(0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_mention_rovo') || !(mentionProvider !== null && mentionProvider !== void 0 && mentionProvider.getRovoChatAgentIdentityAccountId))) {
327
+ if (!(this.lastMentionProviderForRovoIdentity === mentionProvider)) {
312
328
  _context2.next = 2;
313
329
  break;
314
330
  }
315
- this.setRovoChatStyles(false);
316
331
  return _context2.abrupt("return");
317
332
  case 2:
318
- _context2.next = 3;
319
- return mentionProvider.getRovoChatAgentIdentityAccountId();
333
+ this.lastMentionProviderForRovoIdentity = mentionProvider;
334
+ if (!(!(0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_mention_rovo') || !(mentionProvider !== null && mentionProvider !== void 0 && mentionProvider.getRovoChatAgentIdentityAccountId))) {
335
+ _context2.next = 3;
336
+ break;
337
+ }
338
+ this.setRovoChatStyles(false);
339
+ return _context2.abrupt("return");
320
340
  case 3:
341
+ _context2.prev = 3;
342
+ _context2.next = 4;
343
+ return mentionProvider.getRovoChatAgentIdentityAccountId();
344
+ case 4:
321
345
  identityAccountId = _context2.sent;
322
- if (!(this.rovoChatIdentityProvider !== mentionProvider)) {
323
- _context2.next = 4;
346
+ _context2.next = 6;
347
+ break;
348
+ case 5:
349
+ _context2.prev = 5;
350
+ _t = _context2["catch"](3);
351
+ case 6:
352
+ if (!(this.isDestroyed || this.lastMentionProviderForRovoIdentity !== mentionProvider)) {
353
+ _context2.next = 7;
324
354
  break;
325
355
  }
326
356
  return _context2.abrupt("return");
327
- case 4:
357
+ case 7:
358
+ if (!(0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_mention_rovo')) {
359
+ _context2.next = 8;
360
+ break;
361
+ }
362
+ // Recheck after awaiting: newer cached identity must take precedence over this lookup's result.
363
+ this.setRovoChatStyles(this.isRovoChatFromCache() || identityAccountId !== undefined && identityAccountId === this.node.attrs.id);
364
+ return _context2.abrupt("return");
365
+ case 8:
328
366
  if (!(identityAccountId === undefined || identityAccountId !== this.node.attrs.id)) {
329
- _context2.next = 5;
367
+ _context2.next = 9;
330
368
  break;
331
369
  }
332
370
  this.setRovoChatStyles(false);
333
371
  return _context2.abrupt("return");
334
- case 5:
372
+ case 9:
335
373
  this.setRovoChatStyles(true);
336
- case 6:
374
+ case 10:
337
375
  case "end":
338
376
  return _context2.stop();
339
377
  }
340
- }, _callee2, this);
378
+ }, _callee2, this, [[3, 5]]);
341
379
  }));
342
380
  function resolveRovoChatIdentity(_x4) {
343
381
  return _resolveRovoChatIdentity.apply(this, arguments);
@@ -348,12 +386,17 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
348
386
  key: "setRovoChatStyles",
349
387
  value: function setRovoChatStyles(isRovoChat) {
350
388
  var _this$cleanupRovoChat, _this$mentionTextElem2;
389
+ // Avoid resetting styles and rebinding mouse listeners when the state is unchanged.
390
+ if (this.hasRovoChatStyles === isRovoChat) {
391
+ return;
392
+ }
351
393
  (_this$cleanupRovoChat = this.cleanupRovoChatStyles) === null || _this$cleanupRovoChat === void 0 || _this$cleanupRovoChat.call(this);
352
394
  this.cleanupRovoChatStyles = undefined;
353
395
  var primitive = this.mentionPrimitiveElement;
354
396
  if (!primitive) {
355
397
  return;
356
398
  }
399
+ this.hasRovoChatStyles = isRovoChat;
357
400
  primitive.classList.toggle('mention-rovo-chat', isRovoChat);
358
401
  if (!isRovoChat) {
359
402
  var _this$mentionTextElem;
@@ -372,6 +415,10 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
372
415
  setBackground(baseBackground);
373
416
  primitive.style.setProperty('color', inverseText);
374
417
  (_this$mentionTextElem2 = this.mentionTextElement) === null || _this$mentionTextElem2 === void 0 || _this$mentionTextElem2.style.setProperty('color', inverseText);
418
+ // SSR needs the styles above; mouse listeners are attached only on the client.
419
+ if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_mention_rovo') && (0, _isSsr.isSSR)()) {
420
+ return;
421
+ }
375
422
  var onMouseEnter = function onMouseEnter() {
376
423
  return setBackground(hoveredBackground);
377
424
  };
@@ -555,8 +602,8 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
555
602
  }, {
556
603
  key: "shouldHighlightMention",
557
604
  value: function shouldHighlightMention(mentionProvider) {
558
- var _this$config$options;
559
- var _ref4 = (_this$config$options = this.config.options) !== null && _this$config$options !== void 0 ? _this$config$options : {},
605
+ var _this$config$options2;
606
+ var _ref4 = (_this$config$options2 = this.config.options) !== null && _this$config$options2 !== void 0 ? _this$config$options2 : {},
560
607
  currentUserId = _ref4.currentUserId;
561
608
  // Check options first (immediate), then provider (async), then default to false
562
609
  if (currentUserId && this.node.attrs.id === currentUserId) {
@@ -572,7 +619,7 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
572
619
  key: "updateState",
573
620
  value: function () {
574
621
  var _updateState = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(mentionProvider) {
575
- var _this$config$options2;
622
+ var _this$config$options3;
576
623
  var isAgentMentionsEnabled, isHighlighted, disabledState, isDisabled, newState, disabledTooltip, name, text;
577
624
  return _regenerator.default.wrap(function (_context3) {
578
625
  while (1) switch (_context3.prev = _context3.next) {
@@ -599,7 +646,7 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
599
646
  // Only overwrite the disabled-state aria-label with the name-based one
600
647
  // when the chip is NOT disabled; otherwise the disabled reason set in
601
648
  // `setClassList` would be silently clobbered, regressing a11y.
602
- if (this.domElement && (_this$config$options2 = this.config.options) !== null && _this$config$options2 !== void 0 && _this$config$options2.profilecardProvider && newState !== 'disabled') {
649
+ if (this.domElement && (_this$config$options3 = this.config.options) !== null && _this$config$options3 !== void 0 && _this$config$options3.profilecardProvider && newState !== 'disabled') {
603
650
  text = name !== null && name !== void 0 ? name : this.node.attrs.text;
604
651
  if (text && !isAgentAaidText(this.node, isAgentMentionsEnabled)) {
605
652
  this.domElement.setAttribute('aria-label', getAccessibilityLabelFromName(text));
@@ -619,8 +666,8 @@ var MentionNodeView = exports.MentionNodeView = /*#__PURE__*/function () {
619
666
  }, {
620
667
  key: "nodeIsEqual",
621
668
  value: function nodeIsEqual(nextNode) {
622
- var _this$config$options3;
623
- if ((_this$config$options3 = this.config.options) !== null && _this$config$options3 !== void 0 && _this$config$options3.sanitizePrivateContent) {
669
+ var _this$config$options4;
670
+ if ((_this$config$options4 = this.config.options) !== null && _this$config$options4 !== void 0 && _this$config$options4.sanitizePrivateContent) {
624
671
  // Compare nodes but ignore the text parameter as it may be sanitized
625
672
  var nextNodeAttrs = _objectSpread(_objectSpread({}, nextNode.attrs), {}, {
626
673
  text: this.node.attrs.text
@@ -45,7 +45,7 @@ var ACTIONS = exports.ACTIONS = {
45
45
  CLEAR_PENDING_PASTED_AGENT_MENTION: 'CLEAR_PENDING_PASTED_AGENT_MENTION'
46
46
  };
47
47
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
48
- var PACKAGE_VERSION = "30.0.2";
48
+ var PACKAGE_VERSION = "31.0.0";
49
49
  var setProvider = function setProvider(provider) {
50
50
  return function (state, dispatch) {
51
51
  if (dispatch) {
@@ -12,6 +12,8 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
12
12
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
13
13
  var _react = _interopRequireDefault(require("react"));
14
14
  var _uuid = require("uuid");
15
+ var _enums = require("@atlaskit/editor-common/analytics/types/enums");
16
+ var _card = require("@atlaskit/editor-common/card");
15
17
  var _mentions = require("@atlaskit/editor-common/messages/mentions");
16
18
  var _typeAhead = require("@atlaskit/editor-common/type-ahead");
17
19
  var _model = require("@atlaskit/editor-prosemirror/model");
@@ -655,7 +657,14 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
655
657
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
656
658
  sessionId = (0, _uuid.v4)();
657
659
  if (mentionProvider && (0, _utils3.isTeamType)(userType)) {
658
- return insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
660
+ var teamMentionTr = insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
661
+ if ((0, _fg.fg)('platform_editor_team_mention_link_analytics')) {
662
+ return (0, _card.addLinkMetadata)(state.selection, teamMentionTr, {
663
+ action: _enums.ACTION.INSERTED,
664
+ inputMethod: _enums.INPUT_METHOD.MENTION_TYPEAHEAD
665
+ });
666
+ }
667
+ return teamMentionTr;
659
668
  }
660
669
  if (!isAgentMentionInsertion && isXProductUser && mentionProvider && mentionProvider.inviteXProductUser) {
661
670
  mentionProvider.inviteXProductUser(id, name);
@@ -43,6 +43,9 @@ const isMentionTextAnAaid = text => AAID_MENTION_TEXT_PATTERN.test(text);
43
43
  const isAgentMentionsExperimentEnabled = () => expVal('platform_editor_agent_mentions', 'isEnabled', false);
44
44
  const isAgentMentionUserType = userType => userType === 'APP' || userType === 'AGENT';
45
45
 
46
+ // In SSR `HTMLElement` is not defined, so we need to use duck typing here, follow the same pattern used from the emoji and layout plugins.
47
+ const isHTMLElement = node => isExperimentEnabled('platform_editor_mention_rovo') && isSSR() ? 'innerHTML' in node && 'style' in node && 'classList' in node : node instanceof HTMLElement;
48
+
46
49
  /**
47
50
  * Returns true if the node is an APP/AGENT mention whose stored text is a raw
48
51
  * AAID (e.g. '@712020:uuid') rather than a resolved display name.
@@ -132,6 +135,7 @@ const getNewState = (isHighlighted, isRestricted, isDisabled) => {
132
135
  export class MentionNodeView {
133
136
  constructor(node, config) {
134
137
  var _this$domElement$quer, _api$mention$sharedSt;
138
+ // Undefined until the initial styling state has been applied.
135
139
  _defineProperty(this, "hasAvatarSlot", false);
136
140
  _defineProperty(this, "isDestroyed", false);
137
141
  _defineProperty(this, "reportAvatarFailure", reason => {
@@ -172,7 +176,7 @@ export class MentionNodeView {
172
176
  if (canReportMissingProvider && isAvatarEnabled) {
173
177
  this.reportAvatarFailure('missing_provider');
174
178
  }
175
- this.domElement = dom instanceof HTMLElement ? dom : undefined;
179
+ this.domElement = isHTMLElement(dom) ? dom : undefined;
176
180
  this.mentionPrimitiveElement = this.domElement ? (_this$domElement$quer = this.domElement.querySelector(`.${primitiveClassName}`)) !== null && _this$domElement$quer !== void 0 ? _this$domElement$quer : undefined : undefined;
177
181
  this.mentionTextElement = this.mentionPrimitiveElement;
178
182
  if (this.hasAvatarSlot) {
@@ -254,17 +258,40 @@ export class MentionNodeView {
254
258
  }
255
259
  }
256
260
  }
261
+ isRovoChatFromCache() {
262
+ var _this$config$options, _this$config$options$, _this$config$options$2;
263
+ return isExperimentEnabled('platform_editor_mention_rovo') && ((_this$config$options = this.config.options) === null || _this$config$options === void 0 ? void 0 : (_this$config$options$ = _this$config$options.mentionNodeDataProvider) === null || _this$config$options$ === void 0 ? void 0 : (_this$config$options$2 = _this$config$options$.getMentionDataFromCache({
264
+ id: this.node.attrs.id,
265
+ userType: this.node.attrs.userType
266
+ })) === null || _this$config$options$2 === void 0 ? void 0 : _this$config$options$2.isRovoChat) === true;
267
+ }
257
268
  async resolveRovoChatIdentity(mentionProvider) {
258
- if (this.rovoChatIdentityProvider === mentionProvider) {
269
+ // Apply cached identity immediately so SSR includes Rovo styling before the async lookup.
270
+ if (this.isRovoChatFromCache()) {
271
+ this.lastMentionProviderForRovoIdentity = mentionProvider;
272
+ this.setRovoChatStyles(true);
273
+ return;
274
+ }
275
+ if (this.lastMentionProviderForRovoIdentity === mentionProvider) {
259
276
  return;
260
277
  }
261
- this.rovoChatIdentityProvider = mentionProvider;
278
+ this.lastMentionProviderForRovoIdentity = mentionProvider;
262
279
  if (!isExperimentEnabled('platform_editor_mention_rovo') || !(mentionProvider !== null && mentionProvider !== void 0 && mentionProvider.getRovoChatAgentIdentityAccountId)) {
263
280
  this.setRovoChatStyles(false);
264
281
  return;
265
282
  }
266
- const identityAccountId = await mentionProvider.getRovoChatAgentIdentityAccountId();
267
- if (this.rovoChatIdentityProvider !== mentionProvider) {
283
+ let identityAccountId;
284
+ try {
285
+ identityAccountId = await mentionProvider.getRovoChatAgentIdentityAccountId();
286
+ } catch {
287
+ // Fall back to cached identity or ordinary mention styling if the lookup fails.
288
+ }
289
+ if (this.isDestroyed || this.lastMentionProviderForRovoIdentity !== mentionProvider) {
290
+ return;
291
+ }
292
+ if (isExperimentEnabled('platform_editor_mention_rovo')) {
293
+ // Recheck after awaiting: newer cached identity must take precedence over this lookup's result.
294
+ this.setRovoChatStyles(this.isRovoChatFromCache() || identityAccountId !== undefined && identityAccountId === this.node.attrs.id);
268
295
  return;
269
296
  }
270
297
  if (identityAccountId === undefined || identityAccountId !== this.node.attrs.id) {
@@ -275,12 +302,17 @@ export class MentionNodeView {
275
302
  }
276
303
  setRovoChatStyles(isRovoChat) {
277
304
  var _this$cleanupRovoChat, _this$mentionTextElem2;
305
+ // Avoid resetting styles and rebinding mouse listeners when the state is unchanged.
306
+ if (this.hasRovoChatStyles === isRovoChat) {
307
+ return;
308
+ }
278
309
  (_this$cleanupRovoChat = this.cleanupRovoChatStyles) === null || _this$cleanupRovoChat === void 0 ? void 0 : _this$cleanupRovoChat.call(this);
279
310
  this.cleanupRovoChatStyles = undefined;
280
311
  const primitive = this.mentionPrimitiveElement;
281
312
  if (!primitive) {
282
313
  return;
283
314
  }
315
+ this.hasRovoChatStyles = isRovoChat;
284
316
  primitive.classList.toggle('mention-rovo-chat', isRovoChat);
285
317
  if (!isRovoChat) {
286
318
  var _this$mentionTextElem;
@@ -299,6 +331,10 @@ export class MentionNodeView {
299
331
  setBackground(baseBackground);
300
332
  primitive.style.setProperty('color', inverseText);
301
333
  (_this$mentionTextElem2 = this.mentionTextElement) === null || _this$mentionTextElem2 === void 0 ? void 0 : _this$mentionTextElem2.style.setProperty('color', inverseText);
334
+ // SSR needs the styles above; mouse listeners are attached only on the client.
335
+ if (isExperimentEnabled('platform_editor_mention_rovo') && isSSR()) {
336
+ return;
337
+ }
302
338
  const onMouseEnter = () => setBackground(hoveredBackground);
303
339
  const onMouseLeave = () => setBackground(baseBackground);
304
340
  const onMouseDown = () => setBackground(pressedBackground);
@@ -456,10 +492,10 @@ export class MentionNodeView {
456
492
  });
457
493
  }
458
494
  shouldHighlightMention(mentionProvider) {
459
- var _this$config$options;
495
+ var _this$config$options2;
460
496
  const {
461
497
  currentUserId
462
- } = (_this$config$options = this.config.options) !== null && _this$config$options !== void 0 ? _this$config$options : {};
498
+ } = (_this$config$options2 = this.config.options) !== null && _this$config$options2 !== void 0 ? _this$config$options2 : {};
463
499
  // Check options first (immediate), then provider (async), then default to false
464
500
  if (currentUserId && this.node.attrs.id === currentUserId) {
465
501
  return true;
@@ -471,7 +507,7 @@ export class MentionNodeView {
471
507
  }
472
508
  }
473
509
  async updateState(mentionProvider) {
474
- var _this$config$options2;
510
+ var _this$config$options3;
475
511
  const isAgentMentionsEnabled = isAgentMentionsExperimentEnabled();
476
512
  const isHighlighted = this.shouldHighlightMention(mentionProvider);
477
513
  const disabledState = this.getDisabledState(mentionProvider);
@@ -492,7 +528,7 @@ export class MentionNodeView {
492
528
  // Only overwrite the disabled-state aria-label with the name-based one
493
529
  // when the chip is NOT disabled; otherwise the disabled reason set in
494
530
  // `setClassList` would be silently clobbered, regressing a11y.
495
- if (this.domElement && (_this$config$options2 = this.config.options) !== null && _this$config$options2 !== void 0 && _this$config$options2.profilecardProvider && newState !== 'disabled') {
531
+ if (this.domElement && (_this$config$options3 = this.config.options) !== null && _this$config$options3 !== void 0 && _this$config$options3.profilecardProvider && newState !== 'disabled') {
496
532
  const text = name !== null && name !== void 0 ? name : this.node.attrs.text;
497
533
  if (text && !isAgentAaidText(this.node, isAgentMentionsEnabled)) {
498
534
  this.domElement.setAttribute('aria-label', getAccessibilityLabelFromName(text));
@@ -500,8 +536,8 @@ export class MentionNodeView {
500
536
  }
501
537
  }
502
538
  nodeIsEqual(nextNode) {
503
- var _this$config$options3;
504
- if ((_this$config$options3 = this.config.options) !== null && _this$config$options3 !== void 0 && _this$config$options3.sanitizePrivateContent) {
539
+ var _this$config$options4;
540
+ if ((_this$config$options4 = this.config.options) !== null && _this$config$options4 !== void 0 && _this$config$options4.sanitizePrivateContent) {
505
541
  // Compare nodes but ignore the text parameter as it may be sanitized
506
542
  const nextNodeAttrs = {
507
543
  ...nextNode.attrs,
@@ -34,7 +34,7 @@ export const ACTIONS = {
34
34
  CLEAR_PENDING_PASTED_AGENT_MENTION: 'CLEAR_PENDING_PASTED_AGENT_MENTION'
35
35
  };
36
36
  const PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
37
- const PACKAGE_VERSION = "30.0.2";
37
+ const PACKAGE_VERSION = "31.0.0";
38
38
  const setProvider = provider => (state, dispatch) => {
39
39
  if (dispatch) {
40
40
  dispatch(state.tr.setMeta(mentionPluginKey, {
@@ -2,6 +2,8 @@ import React from 'react';
2
2
 
3
3
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
4
4
  import { v4 as uuid } from 'uuid';
5
+ import { ACTION, INPUT_METHOD } from '@atlaskit/editor-common/analytics/types/enums';
6
+ import { addLinkMetadata } from '@atlaskit/editor-common/card';
5
7
  import { mentionMessages } from '@atlaskit/editor-common/messages/mentions';
6
8
  import { TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
7
9
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
@@ -620,7 +622,14 @@ export const createTypeAheadConfig = ({
620
622
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
621
623
  sessionId = uuid();
622
624
  if (mentionProvider && isTeamType(userType)) {
623
- return insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
625
+ const teamMentionTr = insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
626
+ if (fg('platform_editor_team_mention_link_analytics')) {
627
+ return addLinkMetadata(state.selection, teamMentionTr, {
628
+ action: ACTION.INSERTED,
629
+ inputMethod: INPUT_METHOD.MENTION_TYPEAHEAD
630
+ });
631
+ }
632
+ return teamMentionTr;
624
633
  }
625
634
  if (!isAgentMentionInsertion && isXProductUser && mentionProvider && mentionProvider.inviteXProductUser) {
626
635
  mentionProvider.inviteXProductUser(id, name);
@@ -57,6 +57,11 @@ var isAgentMentionUserType = function isAgentMentionUserType(userType) {
57
57
  return userType === 'APP' || userType === 'AGENT';
58
58
  };
59
59
 
60
+ // In SSR `HTMLElement` is not defined, so we need to use duck typing here, follow the same pattern used from the emoji and layout plugins.
61
+ var isHTMLElement = function isHTMLElement(node) {
62
+ return isExperimentEnabled('platform_editor_mention_rovo') && isSSR() ? 'innerHTML' in node && 'style' in node && 'classList' in node : node instanceof HTMLElement;
63
+ };
64
+
60
65
  /**
61
66
  * Returns true if the node is an APP/AGENT mention whose stored text is a raw
62
67
  * AAID (e.g. '@712020:uuid') rather than a resolved display name.
@@ -168,6 +173,7 @@ export var MentionNodeView = /*#__PURE__*/function () {
168
173
  _this$domElement$quer,
169
174
  _api$mention$sharedSt;
170
175
  _classCallCheck(this, MentionNodeView);
176
+ // Undefined until the initial styling state has been applied.
171
177
  _defineProperty(this, "hasAvatarSlot", false);
172
178
  _defineProperty(this, "isDestroyed", false);
173
179
  _defineProperty(this, "reportAvatarFailure", function (reason) {
@@ -205,7 +211,7 @@ export var MentionNodeView = /*#__PURE__*/function () {
205
211
  if (canReportMissingProvider && isAvatarEnabled) {
206
212
  this.reportAvatarFailure('missing_provider');
207
213
  }
208
- this.domElement = dom instanceof HTMLElement ? dom : undefined;
214
+ this.domElement = isHTMLElement(dom) ? dom : undefined;
209
215
  this.mentionPrimitiveElement = this.domElement ? (_this$domElement$quer = this.domElement.querySelector(".".concat(primitiveClassName))) !== null && _this$domElement$quer !== void 0 ? _this$domElement$quer : undefined : undefined;
210
216
  this.mentionTextElement = this.mentionPrimitiveElement;
211
217
  if (this.hasAvatarSlot) {
@@ -288,51 +294,83 @@ export var MentionNodeView = /*#__PURE__*/function () {
288
294
  }
289
295
  }
290
296
  }
297
+ }, {
298
+ key: "isRovoChatFromCache",
299
+ value: function isRovoChatFromCache() {
300
+ var _this$config$options;
301
+ return isExperimentEnabled('platform_editor_mention_rovo') && ((_this$config$options = this.config.options) === null || _this$config$options === void 0 || (_this$config$options = _this$config$options.mentionNodeDataProvider) === null || _this$config$options === void 0 || (_this$config$options = _this$config$options.getMentionDataFromCache({
302
+ id: this.node.attrs.id,
303
+ userType: this.node.attrs.userType
304
+ })) === null || _this$config$options === void 0 ? void 0 : _this$config$options.isRovoChat) === true;
305
+ }
291
306
  }, {
292
307
  key: "resolveRovoChatIdentity",
293
308
  value: function () {
294
309
  var _resolveRovoChatIdentity = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(mentionProvider) {
295
- var identityAccountId;
310
+ var identityAccountId, _t;
296
311
  return _regeneratorRuntime.wrap(function (_context2) {
297
312
  while (1) switch (_context2.prev = _context2.next) {
298
313
  case 0:
299
- if (!(this.rovoChatIdentityProvider === mentionProvider)) {
314
+ if (!this.isRovoChatFromCache()) {
300
315
  _context2.next = 1;
301
316
  break;
302
317
  }
318
+ this.lastMentionProviderForRovoIdentity = mentionProvider;
319
+ this.setRovoChatStyles(true);
303
320
  return _context2.abrupt("return");
304
321
  case 1:
305
- this.rovoChatIdentityProvider = mentionProvider;
306
- if (!(!isExperimentEnabled('platform_editor_mention_rovo') || !(mentionProvider !== null && mentionProvider !== void 0 && mentionProvider.getRovoChatAgentIdentityAccountId))) {
322
+ if (!(this.lastMentionProviderForRovoIdentity === mentionProvider)) {
307
323
  _context2.next = 2;
308
324
  break;
309
325
  }
310
- this.setRovoChatStyles(false);
311
326
  return _context2.abrupt("return");
312
327
  case 2:
313
- _context2.next = 3;
314
- return mentionProvider.getRovoChatAgentIdentityAccountId();
328
+ this.lastMentionProviderForRovoIdentity = mentionProvider;
329
+ if (!(!isExperimentEnabled('platform_editor_mention_rovo') || !(mentionProvider !== null && mentionProvider !== void 0 && mentionProvider.getRovoChatAgentIdentityAccountId))) {
330
+ _context2.next = 3;
331
+ break;
332
+ }
333
+ this.setRovoChatStyles(false);
334
+ return _context2.abrupt("return");
315
335
  case 3:
336
+ _context2.prev = 3;
337
+ _context2.next = 4;
338
+ return mentionProvider.getRovoChatAgentIdentityAccountId();
339
+ case 4:
316
340
  identityAccountId = _context2.sent;
317
- if (!(this.rovoChatIdentityProvider !== mentionProvider)) {
318
- _context2.next = 4;
341
+ _context2.next = 6;
342
+ break;
343
+ case 5:
344
+ _context2.prev = 5;
345
+ _t = _context2["catch"](3);
346
+ case 6:
347
+ if (!(this.isDestroyed || this.lastMentionProviderForRovoIdentity !== mentionProvider)) {
348
+ _context2.next = 7;
319
349
  break;
320
350
  }
321
351
  return _context2.abrupt("return");
322
- case 4:
352
+ case 7:
353
+ if (!isExperimentEnabled('platform_editor_mention_rovo')) {
354
+ _context2.next = 8;
355
+ break;
356
+ }
357
+ // Recheck after awaiting: newer cached identity must take precedence over this lookup's result.
358
+ this.setRovoChatStyles(this.isRovoChatFromCache() || identityAccountId !== undefined && identityAccountId === this.node.attrs.id);
359
+ return _context2.abrupt("return");
360
+ case 8:
323
361
  if (!(identityAccountId === undefined || identityAccountId !== this.node.attrs.id)) {
324
- _context2.next = 5;
362
+ _context2.next = 9;
325
363
  break;
326
364
  }
327
365
  this.setRovoChatStyles(false);
328
366
  return _context2.abrupt("return");
329
- case 5:
367
+ case 9:
330
368
  this.setRovoChatStyles(true);
331
- case 6:
369
+ case 10:
332
370
  case "end":
333
371
  return _context2.stop();
334
372
  }
335
- }, _callee2, this);
373
+ }, _callee2, this, [[3, 5]]);
336
374
  }));
337
375
  function resolveRovoChatIdentity(_x4) {
338
376
  return _resolveRovoChatIdentity.apply(this, arguments);
@@ -343,12 +381,17 @@ export var MentionNodeView = /*#__PURE__*/function () {
343
381
  key: "setRovoChatStyles",
344
382
  value: function setRovoChatStyles(isRovoChat) {
345
383
  var _this$cleanupRovoChat, _this$mentionTextElem2;
384
+ // Avoid resetting styles and rebinding mouse listeners when the state is unchanged.
385
+ if (this.hasRovoChatStyles === isRovoChat) {
386
+ return;
387
+ }
346
388
  (_this$cleanupRovoChat = this.cleanupRovoChatStyles) === null || _this$cleanupRovoChat === void 0 || _this$cleanupRovoChat.call(this);
347
389
  this.cleanupRovoChatStyles = undefined;
348
390
  var primitive = this.mentionPrimitiveElement;
349
391
  if (!primitive) {
350
392
  return;
351
393
  }
394
+ this.hasRovoChatStyles = isRovoChat;
352
395
  primitive.classList.toggle('mention-rovo-chat', isRovoChat);
353
396
  if (!isRovoChat) {
354
397
  var _this$mentionTextElem;
@@ -367,6 +410,10 @@ export var MentionNodeView = /*#__PURE__*/function () {
367
410
  setBackground(baseBackground);
368
411
  primitive.style.setProperty('color', inverseText);
369
412
  (_this$mentionTextElem2 = this.mentionTextElement) === null || _this$mentionTextElem2 === void 0 || _this$mentionTextElem2.style.setProperty('color', inverseText);
413
+ // SSR needs the styles above; mouse listeners are attached only on the client.
414
+ if (isExperimentEnabled('platform_editor_mention_rovo') && isSSR()) {
415
+ return;
416
+ }
370
417
  var onMouseEnter = function onMouseEnter() {
371
418
  return setBackground(hoveredBackground);
372
419
  };
@@ -550,8 +597,8 @@ export var MentionNodeView = /*#__PURE__*/function () {
550
597
  }, {
551
598
  key: "shouldHighlightMention",
552
599
  value: function shouldHighlightMention(mentionProvider) {
553
- var _this$config$options;
554
- var _ref4 = (_this$config$options = this.config.options) !== null && _this$config$options !== void 0 ? _this$config$options : {},
600
+ var _this$config$options2;
601
+ var _ref4 = (_this$config$options2 = this.config.options) !== null && _this$config$options2 !== void 0 ? _this$config$options2 : {},
555
602
  currentUserId = _ref4.currentUserId;
556
603
  // Check options first (immediate), then provider (async), then default to false
557
604
  if (currentUserId && this.node.attrs.id === currentUserId) {
@@ -567,7 +614,7 @@ export var MentionNodeView = /*#__PURE__*/function () {
567
614
  key: "updateState",
568
615
  value: function () {
569
616
  var _updateState = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(mentionProvider) {
570
- var _this$config$options2;
617
+ var _this$config$options3;
571
618
  var isAgentMentionsEnabled, isHighlighted, disabledState, isDisabled, newState, disabledTooltip, name, text;
572
619
  return _regeneratorRuntime.wrap(function (_context3) {
573
620
  while (1) switch (_context3.prev = _context3.next) {
@@ -594,7 +641,7 @@ export var MentionNodeView = /*#__PURE__*/function () {
594
641
  // Only overwrite the disabled-state aria-label with the name-based one
595
642
  // when the chip is NOT disabled; otherwise the disabled reason set in
596
643
  // `setClassList` would be silently clobbered, regressing a11y.
597
- if (this.domElement && (_this$config$options2 = this.config.options) !== null && _this$config$options2 !== void 0 && _this$config$options2.profilecardProvider && newState !== 'disabled') {
644
+ if (this.domElement && (_this$config$options3 = this.config.options) !== null && _this$config$options3 !== void 0 && _this$config$options3.profilecardProvider && newState !== 'disabled') {
598
645
  text = name !== null && name !== void 0 ? name : this.node.attrs.text;
599
646
  if (text && !isAgentAaidText(this.node, isAgentMentionsEnabled)) {
600
647
  this.domElement.setAttribute('aria-label', getAccessibilityLabelFromName(text));
@@ -614,8 +661,8 @@ export var MentionNodeView = /*#__PURE__*/function () {
614
661
  }, {
615
662
  key: "nodeIsEqual",
616
663
  value: function nodeIsEqual(nextNode) {
617
- var _this$config$options3;
618
- if ((_this$config$options3 = this.config.options) !== null && _this$config$options3 !== void 0 && _this$config$options3.sanitizePrivateContent) {
664
+ var _this$config$options4;
665
+ if ((_this$config$options4 = this.config.options) !== null && _this$config$options4 !== void 0 && _this$config$options4.sanitizePrivateContent) {
619
666
  // Compare nodes but ignore the text parameter as it may be sanitized
620
667
  var nextNodeAttrs = _objectSpread(_objectSpread({}, nextNode.attrs), {}, {
621
668
  text: this.node.attrs.text
@@ -37,7 +37,7 @@ export var ACTIONS = {
37
37
  CLEAR_PENDING_PASTED_AGENT_MENTION: 'CLEAR_PENDING_PASTED_AGENT_MENTION'
38
38
  };
39
39
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
40
- var PACKAGE_VERSION = "30.0.2";
40
+ var PACKAGE_VERSION = "31.0.0";
41
41
  var setProvider = function setProvider(provider) {
42
42
  return function (state, dispatch) {
43
43
  if (dispatch) {
@@ -9,6 +9,8 @@ import React from 'react';
9
9
 
10
10
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
11
11
  import { v4 as uuid } from 'uuid';
12
+ import { ACTION, INPUT_METHOD } from '@atlaskit/editor-common/analytics/types/enums';
13
+ import { addLinkMetadata } from '@atlaskit/editor-common/card';
12
14
  import { mentionMessages } from '@atlaskit/editor-common/messages/mentions';
13
15
  import { TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
14
16
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
@@ -646,7 +648,14 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref1) {
646
648
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
647
649
  sessionId = uuid();
648
650
  if (mentionProvider && isTeamType(userType)) {
649
- return insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
651
+ var teamMentionTr = insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
652
+ if (fg('platform_editor_team_mention_link_analytics')) {
653
+ return addLinkMetadata(state.selection, teamMentionTr, {
654
+ action: ACTION.INSERTED,
655
+ inputMethod: INPUT_METHOD.MENTION_TYPEAHEAD
656
+ });
657
+ }
658
+ return teamMentionTr;
650
659
  }
651
660
  if (!isAgentMentionInsertion && isXProductUser && mentionProvider && mentionProvider.inviteXProductUser) {
652
661
  mentionProvider.inviteXProductUser(id, name);
@@ -24,7 +24,8 @@ export declare class MentionNodeView implements NodeView {
24
24
  private mentionTextElement;
25
25
  private mentionAvatar;
26
26
  private cleanupRovoChatStyles;
27
- private rovoChatIdentityProvider;
27
+ private lastMentionProviderForRovoIdentity;
28
+ private hasRovoChatStyles;
28
29
  private hasAvatarSlot;
29
30
  private isDestroyed;
30
31
  private disabledTooltip;
@@ -34,6 +35,7 @@ export declare class MentionNodeView implements NodeView {
34
35
  private subscribedProvider;
35
36
  constructor(node: PMNode, config: MentionNodeViewProps);
36
37
  private setClassList;
38
+ private isRovoChatFromCache;
37
39
  private resolveRovoChatIdentity;
38
40
  private setRovoChatStyles;
39
41
  private getDisabledState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-mentions",
3
- "version": "30.0.3",
3
+ "version": "31.0.1",
4
4
  "description": "Mentions plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -24,11 +24,11 @@
24
24
  "@atlaskit/adf-schema": "^57.7.0",
25
25
  "@atlaskit/css": "^1.1.0",
26
26
  "@atlaskit/editor-json-transformer": "^10.0.0",
27
- "@atlaskit/editor-plugin-analytics": "^28.0.0",
28
- "@atlaskit/editor-plugin-base": "^29.0.0",
29
- "@atlaskit/editor-plugin-context-identifier": "^28.0.0",
30
- "@atlaskit/editor-plugin-selection": "^28.0.0",
31
- "@atlaskit/editor-plugin-type-ahead": "^29.0.0",
27
+ "@atlaskit/editor-plugin-analytics": "^29.0.0",
28
+ "@atlaskit/editor-plugin-base": "^30.0.0",
29
+ "@atlaskit/editor-plugin-context-identifier": "^29.0.0",
30
+ "@atlaskit/editor-plugin-selection": "^29.0.0",
31
+ "@atlaskit/editor-plugin-type-ahead": "^30.0.0",
32
32
  "@atlaskit/editor-prosemirror": "^9.0.0",
33
33
  "@atlaskit/feature-gate-js-client": "^7.0.0",
34
34
  "@atlaskit/icon": "^38.0.0",
@@ -36,14 +36,14 @@
36
36
  "@atlaskit/link": "^5.1.0",
37
37
  "@atlaskit/lozenge": "^17.0.0",
38
38
  "@atlaskit/mention": "^30.3.0",
39
- "@atlaskit/node-data-provider": "^29.0.0",
39
+ "@atlaskit/node-data-provider": "^30.0.0",
40
40
  "@atlaskit/platform-feature-experiments": "^0.3.0",
41
41
  "@atlaskit/platform-feature-flags": "^2.2.0",
42
42
  "@atlaskit/popper": "^9.4.0",
43
43
  "@atlaskit/popup": "^10.0.0",
44
44
  "@atlaskit/portal": "^7.0.0",
45
45
  "@atlaskit/primitives": "^22.6.0",
46
- "@atlaskit/profilecard": "^27.2.0",
46
+ "@atlaskit/profilecard": "^27.3.0",
47
47
  "@atlaskit/section-message": "^10.2.0",
48
48
  "@atlaskit/teams-app-config": "^3.1.0",
49
49
  "@atlaskit/theme": "^28.2.0",
@@ -60,14 +60,14 @@
60
60
  "uuid": "^11.1.1"
61
61
  },
62
62
  "peerDependencies": {
63
- "@atlaskit/editor-common": "^132.0.0",
63
+ "@atlaskit/editor-common": "^133.0.0",
64
64
  "react": "^18.2.0 || ^19.2.0",
65
65
  "react-dom": "^18.2.0 || ^19.2.0",
66
66
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
67
67
  },
68
68
  "devDependencies": {
69
- "@atlaskit/button": "^25.4.0",
70
- "@atlaskit/editor-core": "^238.0.0",
69
+ "@atlaskit/button": "^25.5.0",
70
+ "@atlaskit/editor-core": "^239.0.0",
71
71
  "@atlassian/workbench": "workspace:*",
72
72
  "@testing-library/react": "^16.3.0",
73
73
  "react": "^19.2.0",
@@ -87,6 +87,9 @@
87
87
  "team-mention-inline-smartlink": {
88
88
  "type": "boolean"
89
89
  },
90
+ "platform_editor_team_mention_link_analytics": {
91
+ "type": "boolean"
92
+ },
90
93
  "platform_editor_mention_provider_via_plugin_config": {
91
94
  "type": "boolean"
92
95
  },