UNPKG

@atlaskit/editor-plugin-mentions

Version:

Mentions plugin for @atlaskit/editor-core

707 lines (690 loc) 38.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.ACTIONS = void 0; exports.createMentionPlugin = createMentionPlugin; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _analytics = require("@atlaskit/editor-common/analytics"); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _insm = require("@atlaskit/insm"); var _resource = require("@atlaskit/mention/resource"); var _types = require("@atlaskit/mention/types"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _experiments = require("@atlaskit/tmp-editor-statsig/experiments"); var _mentionNodeView = require("../nodeviews/mentionNodeView"); var _types2 = require("../types"); var _key = require("./key"); var _utils = require("./utils"); function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } 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) { (0, _defineProperty2.default)(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; } var ACTIONS = exports.ACTIONS = { COMMIT_PENDING_TYPED_AGENT_MENTION: 'COMMIT_PENDING_TYPED_AGENT_MENTION', SET_PENDING_TYPED_AGENT_MENTION: 'SET_PENDING_TYPED_AGENT_MENTION', SET_PROVIDER: 'SET_PROVIDER' }; // 'AGENT' is not in the ADF schema UserType enum but is used at runtime. var AGENT_USER_TYPES = new Set(['APP', 'AGENT']); var isAgentUserType = function isAgentUserType(userType) { return typeof userType === 'string' && AGENT_USER_TYPES.has(userType); }; var getAgentMentionName = function getAgentMentionName(text, fallbackName) { var trimmedFallbackName = typeof fallbackName === 'string' ? fallbackName.trim() : ''; var normalizedFallbackName = (trimmedFallbackName.startsWith('@') ? trimmedFallbackName.slice(1).trim() : trimmedFallbackName) || null; if (typeof text !== 'string') { return normalizedFallbackName; } var trimmedText = text.trim(); var displayName = trimmedText.startsWith('@') ? trimmedText.slice(1).trim() : trimmedText; var normalizedName = displayName || normalizedFallbackName; return normalizedName; }; var AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation'; var AGENT_MENTION_INACTIVITY_MS = 3000; var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions"; var PACKAGE_VERSION = "14.4.5"; var setProvider = function setProvider(provider) { return function (state, dispatch) { if (dispatch) { dispatch(state.tr.setMeta(_key.mentionPluginKey, { action: ACTIONS.SET_PROVIDER, params: { provider: provider } })); } return true; }; }; /** * Returns true when a transaction represents a local user document edit that * should restart pending agent-mention inactivity tracking. * * Remote/collab updates, replace-document transactions, AI streaming transforms, * selection-only movements, and metadata-only transactions are intentionally ignored. */ var isQualifyingLocalUserDocChange = function isQualifyingLocalUserDocChange(tr) { var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY)); return tr.docChanged && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming; }; var isLocalSelectionChange = function isLocalSelectionChange(tr, hasPositionChanged) { var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY)); // Pressing Enter can move selection through a doc split without setting tr.selectionSet // or changing from/to numerically, so local doc changes are checked against the // pending mention's current parent before publishing. return (hasPositionChanged || tr.docChanged) && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming; }; /** * Reads agent-mention details from a known document position without traversing * the document. Callers pass a matcher so mapped positions are only accepted * when they still point at the same pending/tracked mention. */ var getAgentMentionDetailsAtPos = function getAgentMentionDetailsAtPos(state, pos, matchesMention, fallbackName) { var _parentNode$type$name; if (pos < 0 || pos > state.doc.content.size) { return null; } var node = state.doc.nodeAt(pos); var mentionSchema = state.schema.nodes.mention; if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !isAgentUserType(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id) { return null; } var $mentionPos = state.doc.resolve(Math.min(pos + node.nodeSize, state.doc.content.size)); var parentNode = $mentionPos.node($mentionPos.depth); return { id: node.attrs.id, context: parentNode.textContent.trim() || null, name: getAgentMentionName(node.attrs.text, fallbackName), nodeSize: node.nodeSize, parentEnd: $mentionPos.end($mentionPos.depth), parentNodeType: (_parentNode$type$name = parentNode.type.name) !== null && _parentNode$type$name !== void 0 ? _parentNode$type$name : null, parentStart: $mentionPos.start($mentionPos.depth), pos: pos }; }; /** * Finds an agent mention that survived a document change when the changed-range * scan did not find one. Prefers the previously tracked mention ID when present; * otherwise returns a surviving agent mention using the existing traversal order. */ var getSurvivingAgentMentionDetails = function getSurvivingAgentMentionDetails(state, preferredId, preferredName) { var mentionSchema = state.schema.nodes.mention; var result = null; state.doc.descendants(function (node, pos) { var _result, _result2; if (((_result = result) === null || _result === void 0 ? void 0 : _result.id) === preferredId) { return false; } if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType) || !node.attrs.id) { return true; } result = getAgentMentionDetailsAtPos(state, pos, function (attrs) { return attrs.id === node.attrs.id; }, node.attrs.id === preferredId ? preferredName : undefined); return ((_result2 = result) === null || _result2 === void 0 ? void 0 : _result2.id) !== preferredId; }); return result; }; /** * Maps a pending typed agent mention through a document-changing transaction and * returns the updated pending state. If the mapped position was deleted or no * longer points at the same local mention, the pending mention is cleared. */ var getPendingTypedAgentMentionAfterDocChange = function getPendingTypedAgentMentionAfterDocChange(state, tr, pendingTypedAgentMention, _ref) { var resetTimer = _ref.resetTimer; var mappedPos = tr.mapping.mapResult(pendingTypedAgentMention.pos, 1); var resetCount = resetTimer ? pendingTypedAgentMention.resetCount + 1 : pendingTypedAgentMention.resetCount; if (mappedPos.deleted) { return null; } var pendingMentionDetails = getAgentMentionDetailsAtPos(state, mappedPos.pos, function (attrs) { return attrs.localId === pendingTypedAgentMention.localId; }, pendingTypedAgentMention.name); return pendingMentionDetails ? { id: pendingMentionDetails.id, localId: pendingTypedAgentMention.localId, name: pendingMentionDetails.name, nodeSize: pendingMentionDetails.nodeSize, pos: pendingMentionDetails.pos, resetCount: resetCount } : null; }; var hasPendingMentionMovedToNewParent = function hasPendingMentionMovedToNewParent(oldState, tr, previousPendingTypedAgentMention, pendingMentionDetails) { if (!previousPendingTypedAgentMention) { return false; } var previousMentionDetails = getAgentMentionDetailsAtPos(oldState, previousPendingTypedAgentMention.pos, function (attrs) { return attrs.localId === previousPendingTypedAgentMention.localId; }); // Keep the previous parent boundary associated with the left side of an // insertion at that boundary, so typing at the start of the parent does not // look like the pending mention moved into a new parent. var mappedPreviousParentStart = previousMentionDetails && tr.mapping.map(previousMentionDetails.parentStart, -1); return Boolean(previousMentionDetails && mappedPreviousParentStart !== pendingMentionDetails.parentStart); }; var isSelectionOutsideDirectParent = function isSelectionOutsideDirectParent(state, pendingMentionDetails) { return state.selection.from < pendingMentionDetails.parentStart || state.selection.to > pendingMentionDetails.parentEnd; }; /** * Finalises a pending typed agent mention by copying its details into the * public lastInserted* plugin state after the caller has already resolved the * pending mention from the current document. */ var commitResolvedPendingTypedAgentMention = function commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails) { var _pluginState$lastAgen; return { hasPublicPluginStateChanged: true, pluginState: _objectSpread(_objectSpread({}, pluginState), {}, { pendingTypedAgentMention: null, lastInsertedAgentMentionId: pendingMentionDetails.id, lastInsertedAgentMentionContext: pendingMentionDetails.context, lastInsertedAgentMentionName: pendingMentionDetails.name, lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType, lastAgentMentionInsertionCount: ((_pluginState$lastAgen = pluginState.lastAgentMentionInsertionCount) !== null && _pluginState$lastAgen !== void 0 ? _pluginState$lastAgen : 0) + 1 }) }; }; /** * Resolves and finalises a pending typed agent mention. If the tracked mention * no longer resolves, the stale pending state is cleared without dispatching a * public update. */ var commitPendingTypedAgentMention = function commitPendingTypedAgentMention(state, pluginState, pendingTypedAgentMention) { var pendingMentionDetails = getAgentMentionDetailsAtPos(state, pendingTypedAgentMention.pos, function (attrs) { return attrs.localId === pendingTypedAgentMention.localId; }, pendingTypedAgentMention.name); if (!pendingMentionDetails) { return { hasPublicPluginStateChanged: false, pluginState: _objectSpread(_objectSpread({}, pluginState), {}, { pendingTypedAgentMention: null }) }; } return commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails); }; var hasTrackedAgentMentionState = function hasTrackedAgentMentionState(pluginState) { return Boolean(pluginState.pendingTypedAgentMention) || pluginState.lastInsertedAgentMentionId != null || pluginState.lastInsertedAgentMentionContext != null || pluginState.lastInsertedAgentMentionName != null || pluginState.lastInsertedAgentMentionParentNodeType != null; }; /** * Clears agent mention state that points at a specific document snapshot. * replaceDocument swaps content wholesale, so pending typed mentions and * lastInserted* details from the previous document must be cleared together. */ var clearTrackedAgentMentionState = function clearTrackedAgentMentionState(pluginState) { return _objectSpread(_objectSpread({}, pluginState), {}, { pendingTypedAgentMention: null, lastInsertedAgentMentionId: null, lastInsertedAgentMentionContext: null, lastInsertedAgentMentionName: null, lastInsertedAgentMentionParentNodeType: null }); }; function createMentionPlugin(_ref2) { var pmPluginFactoryParams = _ref2.pmPluginFactoryParams, fireEvent = _ref2.fireEvent, options = _ref2.options, api = _ref2.api; var mentionProvider; var sendAnalytics = function sendAnalytics(event, actionSubject, action, attributes) { if (event === _resource.SLI_EVENT_TYPE || event === _resource.SMART_EVENT_TYPE) { fireEvent({ action: action, actionSubject: actionSubject, eventType: _analytics.EVENT_TYPE.OPERATIONAL, attributes: _objectSpread({ packageName: PACKAGE_NAME, packageVersion: PACKAGE_VERSION, componentName: _types.ComponentNames.MENTION }, attributes) }, 'fabricElements'); } }; return new _safePlugin.SafePlugin({ key: _key.mentionPluginKey, state: { init: function init(_, state) { var canInsertMention = (0, _utils.canMentionBeCreatedInRange)(state.selection.from, state.selection.to)(state); return { canInsertMention: canInsertMention }; }, apply: function apply(tr, pluginState, oldState, newState) { var _ref3 = tr.getMeta(_key.mentionPluginKey) || { action: null, params: null }, action = _ref3.action, params = _ref3.params; var hasPublicPluginStateChanged = false; var newPluginState = pluginState; var isAgentMentionsExperimentEnabled = (0, _experiments.editorExperiment)('platform_editor_agent_mentions', true); var hasPositionChanged = oldState.selection.from !== newState.selection.from || oldState.selection.to !== newState.selection.to; if (tr.docChanged || tr.selectionSet && hasPositionChanged) { newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, { canInsertMention: (0, _utils.canMentionBeCreatedInRange)(newState.selection.from, newState.selection.to)(newState) }); hasPublicPluginStateChanged = true; } switch (action) { case ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION: { var pendingTypedAgentMention = newPluginState.pendingTypedAgentMention; // Ignore stale timer callbacks. The localId and resetCount must still match the // current pending mention so older timers cannot publish after later user edits. if (!isAgentMentionsExperimentEnabled || !pendingTypedAgentMention || pendingTypedAgentMention.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMention.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) { break; } var commitResult = commitPendingTypedAgentMention(newState, newPluginState, pendingTypedAgentMention); newPluginState = commitResult.pluginState; hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged; break; } case ACTIONS.SET_PROVIDER: newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, { mentionProvider: params.provider }); hasPublicPluginStateChanged = true; break; } // When the agent mentions experiment is off, dispatch immediately (original behaviour). // When it's on, defer dispatch to after the agent tracking block below so that // agent-mention state changes are included in the notification. if (hasPublicPluginStateChanged && !isAgentMentionsExperimentEnabled) { pmPluginFactoryParams.dispatch(_key.mentionPluginKey, newPluginState); } if (options !== null && options !== void 0 && options.handleMentionsChanged && tr.docChanged) { var _insm$session, _insm$session2; (_insm$session = _insm.insm.session) === null || _insm$session === void 0 || _insm$session.startFeature('mentionDeletionDetection'); var mentionSchema = newState.schema.nodes.mention; var mentionsRemoved = new Map(); tr.steps.forEach(function (step, index) { step.getMap().forEach(function (from, to) { var newStart = tr.mapping.slice(index).map(from, -1); var newEnd = tr.mapping.slice(index).map(to); var oldStart = tr.mapping.invert().map(newStart, -1); var oldEnd = tr.mapping.invert().map(newEnd); var oldSlice = oldState.doc.slice(oldStart, oldEnd); var newSlice = newState.doc.slice(newStart, newEnd); var mentionsBefore = new Map(); var mentionsAfter = new Map(); oldSlice.content.descendants(function (node) { if (node.type.name === mentionSchema.name && node.attrs.localId) { mentionsBefore.set(node.attrs.localId, { id: node.attrs.id, localId: node.attrs.localId }); } }); newSlice.content.descendants(function (node) { if (node.type.name === mentionSchema.name && node.attrs.localId) { mentionsAfter.set(node.attrs.localId, { id: node.attrs.id, localId: node.attrs.localId }); } }); // Determine which mentions were removed in this step mentionsBefore.forEach(function (mention, localId) { if (!mentionsAfter.has(localId)) { mentionsRemoved.set(localId, mention); } }); // Adjust mentionsRemoved by removing any that reappear mentionsAfter.forEach(function (_, localId) { if (mentionsRemoved.has(localId)) { mentionsRemoved.delete(localId); } }); }); }); if (mentionsRemoved.size > 0) { var changes = Array.from(mentionsRemoved.values()).map(function (mention) { return { id: mention.id, localId: mention.localId, type: 'deleted' }; }); options.handleMentionsChanged(changes); } (_insm$session2 = _insm.insm.session) === null || _insm$session2 === void 0 || _insm$session2.endFeature('mentionDeletionDetection'); } if (isAgentMentionsExperimentEnabled && isQualifyingLocalUserDocChange(tr)) { var _mentionSchema = newState.schema.nodes.mention; var newDocRanges = []; var oldDocRanges = []; var stepsTouchMentions = false; tr.steps.forEach(function (step) { var found = false; // Only merge a step's ranges if it actually touched an agent mention, // so unrelated steps (e.g. mark-only changes) don't inflate the scan area. var stepNewRanges = []; var stepOldRanges = []; step.getMap().forEach(function (oldFrom, oldTo, newFrom, newTo) { stepOldRanges.push([oldFrom, oldTo]); stepNewRanges.push([newFrom, newTo]); if (!found) { // Clamp positions: delete-only steps can produce newTo > doc.content.size. var clampedNewFrom = Math.min(newFrom, newState.doc.content.size); var clampedNewTo = Math.min(newTo, newState.doc.content.size); if (clampedNewFrom < clampedNewTo) { newState.doc.nodesBetween(clampedNewFrom, clampedNewTo, function (node) { if (node.type === _mentionSchema && isAgentUserType(node.attrs.userType)) { found = true; } return !found; }); } if (!found) { var clampedOldFrom = Math.min(oldFrom, oldState.doc.content.size); var clampedOldTo = Math.min(oldTo, oldState.doc.content.size); if (clampedOldFrom < clampedOldTo) { oldState.doc.nodesBetween(clampedOldFrom, clampedOldTo, function (node) { if (node.type === _mentionSchema && AGENT_USER_TYPES.has(node.attrs.userType)) { found = true; } return !found; }); } } } }); if (found) { stepsTouchMentions = true; newDocRanges.push.apply(newDocRanges, stepNewRanges); oldDocRanges.push.apply(oldDocRanges, stepOldRanges); } }); var shouldResolveAgentMentionState = stepsTouchMentions || Boolean(newPluginState.lastInsertedAgentMentionId); if (shouldResolveAgentMentionState) { var _newPluginState$lastA, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3, _newPluginState$lastI4; var agentMentionId = null; var agentMentionContext = null; var agentMentionName = null; var agentMentionParentNodeType = null; var newCount = 0; var oldAgentMentionId = null; var oldCount = 0; var pendingTypedAgentMentionDetails = null; if (stepsTouchMentions) { var _iterator = _createForOfIteratorHelper(newDocRanges), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _step$value = (0, _slicedToArray2.default)(_step.value, 2), from = _step$value[0], to = _step$value[1]; var clampedTo = Math.min(to, newState.doc.content.size); if (from >= clampedTo) continue; newState.doc.nodesBetween(from, clampedTo, function (node, pos) { if (node.type !== _mentionSchema || !isAgentUserType(node.attrs.userType)) { return true; } newCount++; if (pendingTypedAgentMentionDetails === null && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) { pendingTypedAgentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) { return attrs.localId === params.localId; }, params.name); } if (agentMentionId === null && node.attrs.id) { var agentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) { return attrs.id === node.attrs.id; }, params === null || params === void 0 ? void 0 : params.name); if (agentMentionDetails) { agentMentionId = agentMentionDetails.id; agentMentionContext = agentMentionDetails.context; agentMentionName = agentMentionDetails.name; agentMentionParentNodeType = agentMentionDetails.parentNodeType; } } return true; }); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } var _iterator2 = _createForOfIteratorHelper(oldDocRanges), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var _step2$value = (0, _slicedToArray2.default)(_step2.value, 2), _from = _step2$value[0], _to = _step2$value[1]; var clampedOldTo = Math.min(_to, oldState.doc.content.size); if (_from >= clampedOldTo) continue; oldState.doc.nodesBetween(_from, clampedOldTo, function (node) { if (node.type !== _mentionSchema || !isAgentUserType(node.attrs.userType)) { return true; } oldCount++; if (oldAgentMentionId === null && node.attrs.id) { oldAgentMentionId = node.attrs.id; } return true; }); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } // When a deletion collapses the new-doc range to a zero-width point, or when // the doc changed but no step covered the tracked mention, the new-doc scan // above finds nothing. Check whether any agent mention survived in the document. var resolvedFromFullDocFallback = false; if (agentMentionId === null && newPluginState.lastInsertedAgentMentionId) { var survivorDetails = getSurvivingAgentMentionDetails(newState, newPluginState.lastInsertedAgentMentionId, newPluginState.lastInsertedAgentMentionName); if (survivorDetails) { agentMentionId = survivorDetails.id; agentMentionContext = survivorDetails.context; agentMentionName = survivorDetails.name; agentMentionParentNodeType = survivorDetails.parentNodeType; resolvedFromFullDocFallback = true; } } var isNewInsertion = agentMentionId !== null && !resolvedFromFullDocFallback && (oldAgentMentionId !== agentMentionId || newCount > oldCount); var isPendingTypedAgentMentionInsertion = isNewInsertion && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && typeof (params === null || params === void 0 ? void 0 : params.localId) === 'string'; var newInsertionCount = isNewInsertion ? ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1 : undefined; var pendingTypedAgentMentionDetailsForState = pendingTypedAgentMentionDetails; if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetailsForState) { var pendingTypedAgentMentionLocalId = params === null || params === void 0 ? void 0 : params.localId; newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, { pendingTypedAgentMention: { id: pendingTypedAgentMentionDetailsForState.id, localId: pendingTypedAgentMentionLocalId, name: pendingTypedAgentMentionDetailsForState.name, nodeSize: pendingTypedAgentMentionDetailsForState.nodeSize, pos: pendingTypedAgentMentionDetailsForState.pos, resetCount: 1 } }); } else if (isPendingTypedAgentMentionInsertion) { // Fallback: if the localId-specific scan missed the typed mention, // publish immediately so the insertion is not dropped. newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, { pendingTypedAgentMention: null, lastInsertedAgentMentionId: agentMentionId, lastInsertedAgentMentionContext: agentMentionContext, lastInsertedAgentMentionName: agentMentionName, lastInsertedAgentMentionParentNodeType: agentMentionParentNodeType }, newInsertionCount !== undefined ? { lastAgentMentionInsertionCount: newInsertionCount } : {}); hasPublicPluginStateChanged = true; } else if (agentMentionId !== ((_newPluginState$lastI = newPluginState.lastInsertedAgentMentionId) !== null && _newPluginState$lastI !== void 0 ? _newPluginState$lastI : null) || agentMentionContext !== ((_newPluginState$lastI2 = newPluginState.lastInsertedAgentMentionContext) !== null && _newPluginState$lastI2 !== void 0 ? _newPluginState$lastI2 : null) || agentMentionName !== ((_newPluginState$lastI3 = newPluginState.lastInsertedAgentMentionName) !== null && _newPluginState$lastI3 !== void 0 ? _newPluginState$lastI3 : null) || agentMentionParentNodeType !== ((_newPluginState$lastI4 = newPluginState.lastInsertedAgentMentionParentNodeType) !== null && _newPluginState$lastI4 !== void 0 ? _newPluginState$lastI4 : null) || newInsertionCount !== undefined) { newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, { lastInsertedAgentMentionId: agentMentionId, lastInsertedAgentMentionContext: agentMentionContext, lastInsertedAgentMentionName: agentMentionName, lastInsertedAgentMentionParentNodeType: agentMentionParentNodeType }, newInsertionCount !== undefined ? { lastAgentMentionInsertionCount: newInsertionCount } : {}); hasPublicPluginStateChanged = true; } } } if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument') && hasTrackedAgentMentionState(newPluginState)) { newPluginState = clearTrackedAgentMentionState(newPluginState); hasPublicPluginStateChanged = true; } if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && tr.docChanged) { newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, { pendingTypedAgentMention: getPendingTypedAgentMentionAfterDocChange(newState, tr, newPluginState.pendingTypedAgentMention, { resetTimer: isQualifyingLocalUserDocChange(tr) }) }); } // Typed agent mentions stay pending while the user is still editing around them, // but leaving the mention's direct parent means they have moved on from that // paragraph/block. Publish immediately in that case instead of waiting for the // inactivity timer. var shouldCheckPendingTypedAgentMentionParent = isLocalSelectionChange(tr, hasPositionChanged); if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && shouldCheckPendingTypedAgentMentionParent) { var _pendingTypedAgentMention = newPluginState.pendingTypedAgentMention; var pendingMentionDetails = getAgentMentionDetailsAtPos(newState, _pendingTypedAgentMention.pos, function (attrs) { return attrs.localId === _pendingTypedAgentMention.localId; }, _pendingTypedAgentMention.name); if (!pendingMentionDetails) { newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, { pendingTypedAgentMention: null }); } else if (hasPendingMentionMovedToNewParent(oldState, tr, pluginState.pendingTypedAgentMention, pendingMentionDetails) || isSelectionOutsideDirectParent(newState, pendingMentionDetails)) { var _commitResult = commitResolvedPendingTypedAgentMention(newPluginState, pendingMentionDetails); newPluginState = _commitResult.pluginState; hasPublicPluginStateChanged = hasPublicPluginStateChanged || _commitResult.hasPublicPluginStateChanged; } } if (hasPublicPluginStateChanged && isAgentMentionsExperimentEnabled) { pmPluginFactoryParams.dispatch(_key.mentionPluginKey, newPluginState); } return newPluginState; } }, props: { nodeViews: { mention: function mention(node) { return new _mentionNodeView.MentionNodeView(node, { options: options, api: api, portalProviderAPI: pmPluginFactoryParams.portalProviderAPI }); } } }, view: function view(editorView) { var isAgentMentionsEnabled = (0, _experiments.editorExperiment)('platform_editor_agent_mentions', true); var pendingTypedAgentMentionTimer; var pendingTypedAgentMentionTimerKey = null; var clearPendingTypedAgentMentionTimer = function clearPendingTypedAgentMentionTimer() { if (pendingTypedAgentMentionTimer) { clearTimeout(pendingTypedAgentMentionTimer); pendingTypedAgentMentionTimer = undefined; } pendingTypedAgentMentionTimerKey = null; }; var schedulePendingTypedAgentMentionTimer = function schedulePendingTypedAgentMentionTimer(mentionPluginState) { if (!isAgentMentionsEnabled) { clearPendingTypedAgentMentionTimer(); return; } var pendingTypedAgentMention = mentionPluginState === null || mentionPluginState === void 0 ? void 0 : mentionPluginState.pendingTypedAgentMention; if (!pendingTypedAgentMention) { clearPendingTypedAgentMentionTimer(); return; } var timerKey = "".concat(pendingTypedAgentMention.localId, ":").concat(pendingTypedAgentMention.resetCount); if (timerKey === pendingTypedAgentMentionTimerKey) { return; } clearPendingTypedAgentMentionTimer(); pendingTypedAgentMentionTimerKey = timerKey; pendingTypedAgentMentionTimer = setTimeout(function () { var _mentionPluginKey$get; var latestPendingTypedAgentMention = (_mentionPluginKey$get = _key.mentionPluginKey.getState(editorView.state)) === null || _mentionPluginKey$get === void 0 ? void 0 : _mentionPluginKey$get.pendingTypedAgentMention; if (!latestPendingTypedAgentMention || latestPendingTypedAgentMention.localId !== pendingTypedAgentMention.localId || latestPendingTypedAgentMention.resetCount !== pendingTypedAgentMention.resetCount) { return; } editorView.dispatch(editorView.state.tr.setMeta(_key.mentionPluginKey, { action: ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION, params: { localId: pendingTypedAgentMention.localId, resetCount: pendingTypedAgentMention.resetCount } })); }, AGENT_MENTION_INACTIVITY_MS); }; var providerHandler = function providerHandler(name, providerPromise) { switch (name) { case 'mentionProvider': if (!providerPromise) { fireEvent({ action: _analytics.ACTION.ERRORED, actionSubject: _analytics.ACTION_SUBJECT.MENTION, actionSubjectId: _analytics.ACTION_SUBJECT_ID.MENTION_PROVIDER, eventType: _analytics.EVENT_TYPE.OPERATIONAL, attributes: { reason: _types2.MENTION_PROVIDER_UNDEFINED } }); return setProvider(undefined)(editorView.state, editorView.dispatch); } providerPromise.then(function (provider) { if (mentionProvider) { mentionProvider.unsubscribe('mentionPlugin'); } mentionProvider = provider; setProvider(provider)(editorView.state, editorView.dispatch); provider.subscribe('mentionPlugin', undefined, undefined, undefined, undefined, sendAnalytics); }).catch(function () { fireEvent({ action: _analytics.ACTION.ERRORED, actionSubject: _analytics.ACTION_SUBJECT.MENTION, actionSubjectId: _analytics.ACTION_SUBJECT_ID.MENTION_PROVIDER, eventType: _analytics.EVENT_TYPE.OPERATIONAL, attributes: { reason: _types2.MENTION_PROVIDER_REJECTED } }); return setProvider(undefined)(editorView.state, editorView.dispatch); }); break; } return; }; var providerViaConfig = (0, _platformFeatureFlags.fg)('platform_editor_mention_provider_via_plugin_config'); if (providerViaConfig && options !== null && options !== void 0 && options.mentionProvider) { providerHandler('mentionProvider', options === null || options === void 0 ? void 0 : options.mentionProvider); } else { pmPluginFactoryParams.providerFactory.subscribe('mentionProvider', providerHandler); } return { update: function update(view, prevState) { var mentionPluginState = _key.mentionPluginKey.getState(view.state); if (mentionPluginState === _key.mentionPluginKey.getState(prevState)) { return; } schedulePendingTypedAgentMentionTimer(mentionPluginState); }, destroy: function destroy() { clearPendingTypedAgentMentionTimer(); if (pmPluginFactoryParams.providerFactory) { pmPluginFactoryParams.providerFactory.unsubscribe('mentionProvider', providerHandler); } if (mentionProvider) { mentionProvider.unsubscribe('mentionPlugin'); } } }; } }); }