UNPKG

@atlaskit/mention

Version:

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

344 lines (334 loc) 13.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.DefaultPresenceParser = exports.DefaultPresenceCache = exports.AbstractPresenceResource = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _logger = _interopRequireDefault(require("../util/logger")); var _MentionResource = require("./MentionResource"); 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 _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } var CacheEntry = /*#__PURE__*/function () { function CacheEntry(pres, timeout) { (0, _classCallCheck2.default)(this, CacheEntry); this.presence = pres; this.expiry = Date.now() + timeout; } return (0, _createClass2.default)(CacheEntry, [{ key: "expired", value: function expired() { return Date.now() > this.expiry; } }]); }(); var AbstractPresenceResource = exports.AbstractPresenceResource = /*#__PURE__*/function (_AbstractResource) { function AbstractPresenceResource() { (0, _classCallCheck2.default)(this, AbstractPresenceResource); return _callSuper(this, AbstractPresenceResource, arguments); } (0, _inherits2.default)(AbstractPresenceResource, _AbstractResource); return (0, _createClass2.default)(AbstractPresenceResource, [{ key: "refreshPresence", value: function refreshPresence(userIds) { throw new Error("not yet implemented.\nParams: userIds=".concat(userIds)); } }, { key: "notifyListeners", value: function notifyListeners(presences) { this.changeListeners.forEach(function (listener, key) { try { listener(presences); } catch (e) { // ignore error from listener (0, _logger.default)("error from listener '".concat(key, "', ignoring"), e); } }); } }]); }(_MentionResource.AbstractResource); var PresenceResource = /*#__PURE__*/function (_AbstractPresenceReso) { function PresenceResource(config) { var _this; (0, _classCallCheck2.default)(this, PresenceResource); _this = _callSuper(this, PresenceResource); if (!config.url) { throw new Error('config.url is a required parameter'); } if (!config.cloudId) { throw new Error('config.cloudId is a required parameter'); } _this.config = config; _this.config.url = PresenceResource.cleanUrl(config.url); _this.presenceCache = config.cache || new DefaultPresenceCache(config.cacheExpiry); _this.presenceParser = config.parser || new DefaultPresenceParser(); return _this; } (0, _inherits2.default)(PresenceResource, _AbstractPresenceReso); return (0, _createClass2.default)(PresenceResource, [{ key: "refreshPresence", value: function refreshPresence(userIds) { var cacheHits = this.presenceCache.getBulk(userIds); this.notifyListeners(cacheHits); var cacheMisses = this.presenceCache.getMissingUserIds(userIds); if (cacheMisses.length) { this.retrievePresence(cacheMisses); } } }, { key: "retrievePresence", value: function retrievePresence(userIds) { var _this2 = this; this.queryDirectoryForPresences(userIds).then(function (res) { return _this2.presenceParser.parse(res); }).then(function (presences) { _this2.notifyListeners(presences); _this2.presenceCache.update(presences); }); } }, { key: "queryDirectoryForPresences", value: function queryDirectoryForPresences(userIds) { var query = { query: "query getPresenceForMentions($organizationId: String!, $userIds: [String!], $productId: String) {\n PresenceBulk(organizationId: $organizationId, product: $productId, userIds: $userIds) {\n userId\n state\n stateMetadata\n }\n }", variables: { organizationId: this.config.cloudId, userIds: userIds } }; if (this.config.productId) { query.variables['productId'] = this.config.productId; } var options = { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(query) }; return fetch(this.config.url, options).then(function (response) { return response.json(); }); } }], [{ key: "cleanUrl", value: function cleanUrl(url) { if (url.substr(-1) !== '/') { url += '/'; } return url; } }]); }(AbstractPresenceResource); var DefaultPresenceCache = exports.DefaultPresenceCache = /*#__PURE__*/function () { function DefaultPresenceCache(cacheTimeout, cacheTrigger) { (0, _classCallCheck2.default)(this, DefaultPresenceCache); this.expiryInMillis = cacheTimeout ? cacheTimeout : DefaultPresenceCache.defaultTimeout; this.flushTrigger = cacheTrigger ? cacheTrigger : DefaultPresenceCache.defaultFlushTrigger; this.cache = {}; this.size = 0; } /** * Precondition: _delete is only called internally if userId exists in cache * Removes cache entry * @param userId */ return (0, _createClass2.default)(DefaultPresenceCache, [{ key: "_delete", value: function _delete(userId) { delete this.cache[userId]; this.size--; } /** * Checks a cache entry and calls delete if the info has expired * @param userId */ }, { key: "_deleteIfExpired", value: function _deleteIfExpired(userId) { if (this.contains(userId) && this.cache[userId].expired()) { this._delete(userId); } } /** * Cleans expired entries from cache */ }, { key: "_removeExpired", value: function _removeExpired() { var _this3 = this; Object.keys(this.cache).forEach(function (id) { _this3._deleteIfExpired(id); }); } /** * Checks if a user exists in the cache * @param userId */ }, { key: "contains", value: function contains(userId) { return this.cache.hasOwnProperty(userId); } /** * Retrieves a presence from the cache after checking for expired entries * @param userId - to index the cache * @returns Presence - the presence that matches the userId */ }, { key: "get", value: function get(userId) { this._deleteIfExpired(userId); if (!this.contains(userId)) { return {}; } return this.cache[userId].presence; } /** * Retrieve multiple presences at once from the cache * @param userIds - to index the cache * @returns PresenceMap - A map of userIds to cached Presences */ }, { key: "getBulk", value: function getBulk(userIds) { var presences = {}; var _iterator = _createForOfIteratorHelper(userIds), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _userId = _step.value; if (this.contains(_userId)) { presences[_userId] = this.get(_userId); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return presences; } /** * For a given list of ids, returns a subset * of all the ids with missing cache entries. * @param userIds - to index the cache * @returns string[] - ids missing from the cache */ }, { key: "getMissingUserIds", value: function getMissingUserIds(userIds) { var _this4 = this; return userIds.filter(function (id) { return !_this4.contains(id); }); } /** * Precondition: presMap only contains ids of users not in cache * expired users must first be removed then reinserted with updated presence * Updates the cache by adding the new Presence entries and setting the expiry time * @param presMap */ }, { key: "update", value: function update(presMap) { var _this5 = this; if (this.size >= this.flushTrigger) { this._removeExpired(); } Object.keys(presMap).forEach(function (userId) { _this5.cache[userId] = new CacheEntry(presMap[userId], _this5.expiryInMillis); _this5.size++; }); } }]); }(); (0, _defineProperty2.default)(DefaultPresenceCache, "defaultTimeout", 20000); (0, _defineProperty2.default)(DefaultPresenceCache, "defaultFlushTrigger", 50); var DefaultPresenceParser = exports.DefaultPresenceParser = /*#__PURE__*/function () { function DefaultPresenceParser() { (0, _classCallCheck2.default)(this, DefaultPresenceParser); } return (0, _createClass2.default)(DefaultPresenceParser, [{ key: "mapState", value: function mapState(state) { if (state === 'unavailable') { return 'offline'; } else if (state === 'available') { return 'online'; } else { return state; } } }, { key: "parse", value: function parse(response) { var presences = {}; if (response.hasOwnProperty('data') && response['data'].hasOwnProperty('PresenceBulk')) { var results = response['data'].PresenceBulk; // Store map of state and time indexed by userId. Ignore null results. var _iterator2 = _createForOfIteratorHelper(results), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var user = _step2.value; if (user.userId && user.state) { var _state = DefaultPresenceParser.extractState(user) || user.state; presences[user.userId] = { status: this.mapState(_state) }; } else if (!user.hasOwnProperty('userId') || !user.hasOwnProperty('state')) { // eslint-disable-next-line no-console console.error('Unexpected response from presence service contains keys: ' + Object.keys(user)); } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } return presences; } }], [{ key: "extractState", value: function extractState(presence) { if (DefaultPresenceParser.isFocusState(presence)) { return DefaultPresenceParser.FOCUS_STATE; } return presence.state; } /* This is a bit of an odd exception. In the case where a user is in "Focus Mode", their presence state is returned as 'busy' along with a `stateMetadata` object containing a `focus` field. In this case we ignore the value of the `state` field and treat the presence as a 'focus' state. */ }, { key: "isFocusState", value: function isFocusState(presence) { if (presence.stateMetadata) { try { var metadata = JSON.parse(presence.stateMetadata); return metadata && !!metadata.focus; } catch (e) { // eslint-disable-next-line no-console console.error("Failed to parse presence's stateMetadata for user with id ".concat(presence.userId, ": ").concat(presence.stateMetadata)); // eslint-disable-next-line no-console console.error(e); } } return false; } }]); }(); (0, _defineProperty2.default)(DefaultPresenceParser, "FOCUS_STATE", 'focus'); var _default = exports.default = PresenceResource;