UNPKG

@atlaskit/mention

Version:

A React component used to display user profiles in a list for 'Mention' functionality

202 lines (197 loc) 7.83 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import { MentionNameStatus } from '../types'; import { fireAnalyticsMentionHydrationEvent } from '../util/analytics'; /** A queue for user ids */ export var DefaultMentionNameResolver = /*#__PURE__*/function () { function DefaultMentionNameResolver(client) { var _this = this; var analyticsProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var onResolvedAll = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {}; _classCallCheck(this, DefaultMentionNameResolver); _defineProperty(this, "nameCache", new Map()); _defineProperty(this, "nameQueue", new Map()); _defineProperty(this, "nameStartTime", new Map()); _defineProperty(this, "processingQueue", new Map()); _defineProperty(this, "debounce", 0); _defineProperty(this, "debounceOnResolve", null); _defineProperty(this, "isOnResolvedAllCalled", false); _defineProperty(this, "processQueue", function () { clearTimeout(_this.debounce); _this.debounce = 0; var _this$splitQueueAtLim = _this.splitQueueAtLimit(), queue = _this$splitQueueAtLim.queue, extraQueue = _this$splitQueueAtLim.extraQueue; _this.nameQueue = extraQueue; _this.processingQueue = mergeNameResolverQueues(_this.processingQueue, queue); _this.client.lookupMentionNames(Array.from(queue.keys())).then(function (response) { response.forEach(function (mentionDetail) { var id = mentionDetail.id; queue.delete(id); _this.resolveQueueItem(mentionDetail); }); queue.forEach(function (_callback, id) { // No response from client for these ids treat as unknown _this.resolveQueueItem({ id: id, status: MentionNameStatus.UNKNOWN }); }); }).catch(function () { // Service completely failed, reject all items in the queue queue.forEach(function (_callback, id) { _this.resolveQueueItem({ id: id, status: MentionNameStatus.SERVICE_ERROR }); }); }); // Make sure anything left in the queue gets processed. if (_this.nameQueue.size > 0) { _this.scheduleProcessQueue(); } else { _this.scheduleOnAllResolved(); } }); this.client = client; this.fireHydrationEvent = fireAnalyticsMentionHydrationEvent(analyticsProps); // If provided, this will be called once all pending mentions in the queue are resolved. // A sample usage is scrolling to a mention on page load, after the mentions have loadad. this.onResolvedAll = onResolvedAll; } return _createClass(DefaultMentionNameResolver, [{ key: "lookupName", value: function lookupName(id) { var _this2 = this; var name = this.nameCache.get(id); if (name) { this.fireAnalytics(true, name); if (this.nameQueue.size === 0) { this.scheduleOnAllResolved(); } return name; } return new Promise(function (resolve) { var processingItems = _this2.processingQueue.get(id); if (processingItems) { _this2.processingQueue.set(id, [].concat(_toConsumableArray(processingItems), [resolve])); } var queuedItems = _this2.nameQueue.get(id) || []; _this2.nameQueue.set(id, [].concat(_toConsumableArray(queuedItems), [resolve])); if (queuedItems.length === 0 && !processingItems) { _this2.nameStartTime.set(id, Date.now()); } _this2.scheduleProcessQueue(); if (_this2.isQueueAtLimit()) { _this2.processQueue(); } }); } }, { key: "cacheName", value: function cacheName(id, name) { this.nameCache.set(id, { id: id, name: name, status: MentionNameStatus.OK }); } }, { key: "scheduleProcessQueue", value: function scheduleProcessQueue() { if (!this.debounce) { this.debounce = window.setTimeout(this.processQueue, DefaultMentionNameResolver.waitForBatch); } } }, { key: "scheduleOnAllResolved", value: function scheduleOnAllResolved() { var _this3 = this; if (this.debounceOnResolve) { clearTimeout(this.debounceOnResolve); } this.debounceOnResolve = window.setTimeout(function () { if (_this3.isOnResolvedAllCalled) { return; } _this3.onResolvedAll(); _this3.isOnResolvedAllCalled = true; }, DefaultMentionNameResolver.waitForResolveAll); } }, { key: "isQueueAtLimit", value: function isQueueAtLimit() { return this.nameQueue.size >= this.client.getLookupLimit(); } }, { key: "splitQueueAtLimit", value: function splitQueueAtLimit() { var values = Array.from(this.nameQueue.entries()); var splitPoint = this.client.getLookupLimit(); return { queue: new Map(values.slice(0, splitPoint)), extraQueue: new Map(values.slice(splitPoint)) }; } }, { key: "resolveQueueItem", value: function resolveQueueItem(mentionDetail) { var id = mentionDetail.id; var resolvers = this.processingQueue.get(id); if (resolvers) { this.processingQueue.delete(id); this.nameCache.set(id, mentionDetail); resolvers.forEach(function (resolve) { try { resolve(mentionDetail); } catch (_unused) { // ignore - exception in consumer } }); this.fireAnalytics(false, mentionDetail); } } }, { key: "fireAnalytics", value: function fireAnalytics(fromCache, mentionDetail) { var id = mentionDetail.id; var action = mentionDetail.status === MentionNameStatus.OK ? 'completed' : 'failed'; var start = this.nameStartTime.get(id); var duration = start ? Date.now() - start : 0; this.nameStartTime.delete(id); this.fireHydrationEvent(action, id, fromCache, duration); } }]); }(); /** * Merge the two queues making sure to merge callback arrays for items in queueB already in queueA. * This addresses [this ticket](https://product-fabric.atlassian.net/browse/QS-3789). */ _defineProperty(DefaultMentionNameResolver, "waitForBatch", 100); // ms _defineProperty(DefaultMentionNameResolver, "waitForResolveAll", 800); export function mergeNameResolverQueues(queueA, queueB) { var queueBeingMerged = new Map(_toConsumableArray(queueA)); // now add the items from the second queue that are not already in the // merged queue being built _toConsumableArray(queueB).forEach(function (item) { var _item = _slicedToArray(item, 2), key = _item[0], queueBCallbacks = _item[1]; var itemAlreadyInMergedQueue = queueBeingMerged.has(key); if (!itemAlreadyInMergedQueue) { queueBeingMerged.set(key, queueBCallbacks); } else { var _queueBeingMerged$get; // item already in merged queue, merge the callback arrays var queueACallbacks = (_queueBeingMerged$get = queueBeingMerged.get(key)) !== null && _queueBeingMerged$get !== void 0 ? _queueBeingMerged$get : []; var mergedCallbacks = new Set([].concat(_toConsumableArray(queueBCallbacks), _toConsumableArray(queueACallbacks))); var deduplicatedCallbacks = Array.from(mergedCallbacks.values()); // prevents calling them twice queueBeingMerged.set(key, deduplicatedCallbacks); } }); return queueBeingMerged; }