UNPKG

stream-chat

Version:

JS SDK for the Stream Chat API

1,335 lines (1,101 loc) 677 kB
import { fromByteArray } from 'base64-js'; import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator'; import _classCallCheck from '@babel/runtime/helpers/classCallCheck'; import _createClass from '@babel/runtime/helpers/createClass'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _regeneratorRuntime from '@babel/runtime/regenerator'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _extends from '@babel/runtime/helpers/extends'; import _typeof from '@babel/runtime/helpers/typeof'; import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import axios from 'axios'; import FormData from 'form-data'; import WebSocket from 'isomorphic-ws'; import _assertThisInitialized from '@babel/runtime/helpers/assertThisInitialized'; import _inherits from '@babel/runtime/helpers/inherits'; import _possibleConstructorReturn from '@babel/runtime/helpers/possibleConstructorReturn'; import _getPrototypeOf from '@babel/runtime/helpers/getPrototypeOf'; import _wrapNativeSuper from '@babel/runtime/helpers/wrapNativeSuper'; function isString$1(arrayOrString) { return typeof arrayOrString === 'string'; } function isMapStringCallback(arrayOrString, callback) { return !!callback && isString$1(arrayOrString); } // source - https://github.com/beatgammit/base64-js/blob/master/test/convert.js#L72 function map(arrayOrString, callback) { var res = []; if (isString$1(arrayOrString) && isMapStringCallback(arrayOrString, callback)) { for (var k = 0, len = arrayOrString.length; k < len; k++) { if (arrayOrString.charAt(k)) { var kValue = arrayOrString.charAt(k); var mappedValue = callback(kValue, k, arrayOrString); res[k] = mappedValue; } } } else if (!isString$1(arrayOrString) && !isMapStringCallback(arrayOrString, callback)) { for (var _k = 0, _len = arrayOrString.length; _k < _len; _k++) { if (_k in arrayOrString) { var _kValue = arrayOrString[_k]; var _mappedValue = callback(_kValue, _k, arrayOrString); res[_k] = _mappedValue; } } } return res; } var encodeBase64 = function encodeBase64(data) { return fromByteArray(new Uint8Array(map(data, function (char) { return char.charCodeAt(0); }))); }; // base-64 decoder throws exception if encoded string is not padded by '=' to make string length // in multiples of 4. So gonna use our own method for this purpose to keep backwards compatibility // https://github.com/beatgammit/base64-js/blob/master/index.js#L26 var decodeBase64 = function decodeBase64(s) { var e = {}, w = String.fromCharCode, L = s.length; var i, b = 0, c, x, l = 0, a, r = ''; var A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; for (i = 0; i < 64; i++) { e[A.charAt(i)] = i; } for (x = 0; x < L; x++) { c = e[s.charAt(x)]; b = (b << 6) + c; l += 6; while (l >= 8) { ((a = b >>> (l -= 8) & 0xff) || x < L - 2) && (r += w(a)); } } return r; }; var Campaign = /*#__PURE__*/function () { function Campaign(client, id, data) { _classCallCheck(this, Campaign); _defineProperty(this, "id", void 0); _defineProperty(this, "data", void 0); _defineProperty(this, "client", void 0); this.client = client; this.id = id; this.data = data; } _createClass(Campaign, [{ key: "create", value: function () { var _create = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() { var _this$data, _this$data2, _this$data3, _this$data4, _this$data5, _this$data6, _this$data7, _this$data8, _this$data9; var body, result; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: body = { id: this.id, message_template: (_this$data = this.data) === null || _this$data === void 0 ? void 0 : _this$data.message_template, segment_ids: (_this$data2 = this.data) === null || _this$data2 === void 0 ? void 0 : _this$data2.segment_ids, sender_id: (_this$data3 = this.data) === null || _this$data3 === void 0 ? void 0 : _this$data3.sender_id, sender_mode: (_this$data4 = this.data) === null || _this$data4 === void 0 ? void 0 : _this$data4.sender_mode, channel_template: (_this$data5 = this.data) === null || _this$data5 === void 0 ? void 0 : _this$data5.channel_template, create_channels: (_this$data6 = this.data) === null || _this$data6 === void 0 ? void 0 : _this$data6.create_channels, description: (_this$data7 = this.data) === null || _this$data7 === void 0 ? void 0 : _this$data7.description, name: (_this$data8 = this.data) === null || _this$data8 === void 0 ? void 0 : _this$data8.name, user_ids: (_this$data9 = this.data) === null || _this$data9 === void 0 ? void 0 : _this$data9.user_ids }; _context.next = 3; return this.client.createCampaign(body); case 3: result = _context.sent; this.id = result.campaign.id; this.data = result.campaign; return _context.abrupt("return", result); case 7: case "end": return _context.stop(); } } }, _callee, this); })); function create() { return _create.apply(this, arguments); } return create; }() }, { key: "verifyCampaignId", value: function verifyCampaignId() { if (!this.id) { throw new Error('Campaign id is missing. Either create the campaign using campaign.create() or set the id during instantiation - const campaign = client.campaign(id)'); } } }, { key: "start", value: function () { var _start = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(options) { return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: this.verifyCampaignId(); _context2.next = 3; return this.client.startCampaign(this.id, options); case 3: return _context2.abrupt("return", _context2.sent); case 4: case "end": return _context2.stop(); } } }, _callee2, this); })); function start(_x) { return _start.apply(this, arguments); } return start; }() }, { key: "update", value: function () { var _update = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(data) { return _regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: this.verifyCampaignId(); return _context3.abrupt("return", this.client.updateCampaign(this.id, data)); case 2: case "end": return _context3.stop(); } } }, _callee3, this); })); function update(_x2) { return _update.apply(this, arguments); } return update; }() }, { key: "delete", value: function () { var _delete2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() { return _regeneratorRuntime.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: this.verifyCampaignId(); _context4.next = 3; return this.client.deleteCampaign(this.id); case 3: return _context4.abrupt("return", _context4.sent); case 4: case "end": return _context4.stop(); } } }, _callee4, this); })); function _delete() { return _delete2.apply(this, arguments); } return _delete; }() }, { key: "stop", value: function () { var _stop = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5() { return _regeneratorRuntime.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: this.verifyCampaignId(); return _context5.abrupt("return", this.client.stopCampaign(this.id)); case 2: case "end": return _context5.stop(); } } }, _callee5, this); })); function stop() { return _stop.apply(this, arguments); } return stop; }() }, { key: "get", value: function () { var _get = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(options) { return _regeneratorRuntime.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: this.verifyCampaignId(); return _context6.abrupt("return", this.client.getCampaign(this.id, options)); case 2: case "end": return _context6.stop(); } } }, _callee6, this); })); function get(_x3) { return _get.apply(this, arguments); } return get; }() }]); return Campaign; }(); var https = null; function ownKeys$e(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread$e(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$e(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$e(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _createForOfIteratorHelper$5(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$5(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function (_e) { function e(_x2) { return _e.apply(this, arguments); } e.toString = function () { return _e.toString(); }; return e; }(function (e) { throw e; }), 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 normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function (_e2) { function e(_x3) { return _e2.apply(this, arguments); } e.toString = function () { return _e2.toString(); }; return e; }(function (e) { didErr = true; err = e; }), f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$5(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$5(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$5(o, minLen); } function _arrayLikeToArray$5(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } /** * logChatPromiseExecution - utility function for logging the execution of a promise.. * use this when you want to run the promise and handle errors by logging a warning * * @param {Promise<T>} promise The promise you want to run and log * @param {string} name A descriptive name of what the promise does for log output * */ function logChatPromiseExecution(promise, name) { promise.then().catch(function (error) { console.warn("failed to do ".concat(name, ", ran into error: "), error); }); } var sleep = function sleep(m) { return new Promise(function (r) { return setTimeout(r, m); }); }; function isFunction(value) { return value && (Object.prototype.toString.call(value) === '[object Function]' || 'function' === typeof value || value instanceof Function); } var chatCodes = { TOKEN_EXPIRED: 40, WS_CLOSED_SUCCESS: 1000 }; function isReadableStream(obj) { return obj !== null && _typeof(obj) === 'object' && (obj.readable || typeof obj._read === 'function'); } function isBuffer(obj) { return obj != null && obj.constructor != null && // @ts-expect-error typeof obj.constructor.isBuffer === 'function' && // @ts-expect-error obj.constructor.isBuffer(obj); } function isFileWebAPI(uri) { return typeof window !== 'undefined' && 'File' in window && uri instanceof File; } function isOwnUser(user) { return (user === null || user === void 0 ? void 0 : user.total_unread_count) !== undefined; } function isBlobWebAPI(uri) { return typeof window !== 'undefined' && 'Blob' in window && uri instanceof Blob; } function isOwnUserBaseProperty(property) { var ownUserBaseProperties = { channel_mutes: true, devices: true, mutes: true, total_unread_count: true, unread_channels: true, unread_count: true, unread_threads: true, invisible: true, privacy_settings: true, roles: true, push_preferences: true }; return ownUserBaseProperties[property]; } function addFileToFormData(uri, name, contentType) { var data = new FormData(); if (isReadableStream(uri) || isBuffer(uri) || isFileWebAPI(uri) || isBlobWebAPI(uri)) { if (name) data.append('file', uri, name);else data.append('file', uri); } else { data.append('file', { uri: uri, name: name || uri.split('/').reverse()[0], contentType: contentType || undefined, type: contentType || undefined }); } return data; } function normalizeQuerySort(sort) { var sortFields = []; var sortArr = Array.isArray(sort) ? sort : [sort]; var _iterator = _createForOfIteratorHelper$5(sortArr), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _item = _step.value; var entries = Object.entries(_item); if (entries.length > 1) { console.warn("client._buildSort() - multiple fields in a single sort object detected. Object's field order is not guaranteed"); } for (var _i = 0, _entries = entries; _i < _entries.length; _i++) { var _entries$_i = _slicedToArray(_entries[_i], 2), field = _entries$_i[0], direction = _entries$_i[1]; sortFields.push({ field: field, direction: direction }); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return sortFields; } /** * retryInterval - A retry interval which increases acc to number of failures * * @return {number} Duration to wait in milliseconds */ function retryInterval(numberOfFailures) { // try to reconnect in 0.25-25 seconds (random to spread out the load from failures) var max = Math.min(500 + numberOfFailures * 2000, 25000); var min = Math.min(Math.max(250, (numberOfFailures - 1) * 2000), 25000); return Math.floor(Math.random() * (max - min) + min); } function randomId() { return generateUUIDv4(); } function hex(bytes) { var s = ''; for (var i = 0; i < bytes.length; i++) { s += bytes[i].toString(16).padStart(2, '0'); } return s; } // https://tools.ietf.org/html/rfc4122 function generateUUIDv4() { var bytes = getRandomBytes(16); bytes[6] = bytes[6] & 0x0f | 0x40; // version bytes[8] = bytes[8] & 0xbf | 0x80; // variant return hex(bytes.subarray(0, 4)) + '-' + hex(bytes.subarray(4, 6)) + '-' + hex(bytes.subarray(6, 8)) + '-' + hex(bytes.subarray(8, 10)) + '-' + hex(bytes.subarray(10, 16)); } function getRandomValuesWithMathRandom(bytes) { var max = Math.pow(2, 8 * bytes.byteLength / bytes.length); for (var i = 0; i < bytes.length; i++) { bytes[i] = Math.random() * max; } } var getRandomValues = function () { var _crypto; if (typeof crypto !== 'undefined' && typeof ((_crypto = crypto) === null || _crypto === void 0 ? void 0 : _crypto.getRandomValues) !== 'undefined') { return crypto.getRandomValues.bind(crypto); } else if (typeof msCrypto !== 'undefined') { return msCrypto.getRandomValues.bind(msCrypto); } else { return getRandomValuesWithMathRandom; } }(); function getRandomBytes(length) { var bytes = new Uint8Array(length); getRandomValues(bytes); return bytes; } function convertErrorToJson(err) { var jsonObj = {}; if (!err) return jsonObj; try { Object.getOwnPropertyNames(err).forEach(function (key) { jsonObj[key] = Object.getOwnPropertyDescriptor(err, key); }); } catch (_) { return { error: 'failed to serialize the error' }; } return jsonObj; } /** * isOnline safely return the navigator.online value for browser env * if navigator is not in global object, it always return true */ function isOnline() { var nav = typeof navigator !== 'undefined' ? navigator : typeof window !== 'undefined' && window.navigator ? window.navigator : undefined; if (!nav) { console.warn('isOnline failed to access window.navigator and assume browser is online'); return true; } // RN navigator has undefined for onLine if (typeof nav.onLine !== 'boolean') { return true; } return nav.onLine; } /** * listenForConnectionChanges - Adds an event listener fired on browser going online or offline */ function addConnectionEventListeners(cb) { if (typeof window !== 'undefined' && window.addEventListener) { window.addEventListener('offline', cb); window.addEventListener('online', cb); } } function removeConnectionEventListeners(cb) { if (typeof window !== 'undefined' && window.removeEventListener) { window.removeEventListener('offline', cb); window.removeEventListener('online', cb); } } var axiosParamsSerializer = function axiosParamsSerializer(params) { var newParams = []; for (var k in params) { // Stream backend doesn't treat "undefined" value same as value not being present. // So, we need to skip the undefined values. if (params[k] === undefined) continue; if (Array.isArray(params[k]) || _typeof(params[k]) === 'object') { newParams.push("".concat(k, "=").concat(encodeURIComponent(JSON.stringify(params[k])))); } else { newParams.push("".concat(k, "=").concat(encodeURIComponent(params[k]))); } } return newParams.join('&'); }; /** * Takes the message object, parses the dates, sets `__html` * and sets the status to `received` if missing; returns a new message object. * * @param {MessageResponse<StreamChatGenerics>} message `MessageResponse` object */ function formatMessage(message) { return _objectSpread$e(_objectSpread$e({}, message), {}, { /** * @deprecated please use `html` */ __html: message.html, // parse the dates pinned_at: message.pinned_at ? new Date(message.pinned_at) : null, created_at: message.created_at ? new Date(message.created_at) : new Date(), updated_at: message.updated_at ? new Date(message.updated_at) : new Date(), deleted_at: message.deleted_at ? new Date(message.deleted_at) : null, status: message.status || 'received', reaction_groups: maybeGetReactionGroupsFallback(message.reaction_groups, message.reaction_counts, message.reaction_scores) }); } var findIndexInSortedArray = function findIndexInSortedArray(_ref) { var needle = _ref.needle, sortedArray = _ref.sortedArray, selectKey = _ref.selectKey, _ref$selectValueToCom = _ref.selectValueToCompare, selectValueToCompare = _ref$selectValueToCom === void 0 ? function (e) { return e; } : _ref$selectValueToCom, _ref$sortDirection = _ref.sortDirection, sortDirection = _ref$sortDirection === void 0 ? 'ascending' : _ref$sortDirection; if (!sortedArray.length) return 0; var left = 0; var right = sortedArray.length - 1; var middle = 0; var recalculateMiddle = function recalculateMiddle() { middle = Math.round((left + right) / 2); }; var comparableNeedle = selectValueToCompare(needle); while (left <= right) { recalculateMiddle(); var comparableMiddle = selectValueToCompare(sortedArray[middle]); if (sortDirection === 'ascending' && comparableNeedle < comparableMiddle || sortDirection === 'descending' && comparableNeedle >= comparableMiddle) { right = middle - 1; } else { left = middle + 1; } } // In case there are several array elements with the same comparable value, search around the insertion // point to possibly find an element with the same key. If found, prefer it. // This, for example, prevents duplication of messages with the same creation date. if (selectKey) { var needleKey = selectKey(needle); var step = sortDirection === 'ascending' ? -1 : +1; for (var i = left + step; 0 <= i && i < sortedArray.length && selectValueToCompare(sortedArray[i]) === comparableNeedle; i += step) { if (selectKey(sortedArray[i]) === needleKey) { return i; } } } return left; }; function addToMessageList(messages, newMessage) { var timestampChanged = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var sortBy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'created_at'; var addIfDoesNotExist = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; var addMessageToList = addIfDoesNotExist || timestampChanged; var newMessages = _toConsumableArray(messages); // if created_at has changed, message should be filtered and re-inserted in correct order // slow op but usually this only happens for a message inserted to state before actual response with correct timestamp if (timestampChanged) { newMessages = newMessages.filter(function (message) { return !(message.id && newMessage.id === message.id); }); } // for empty list just concat and return unless it's an update or deletion if (newMessages.length === 0 && addMessageToList) { return newMessages.concat(newMessage); } else if (newMessages.length === 0) { return newMessages; } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var messageTime = newMessage[sortBy].getTime(); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var messageIsNewest = newMessages.at(-1)[sortBy].getTime() < messageTime; // if message is newer than last item in the list concat and return unless it's an update or deletion if (messageIsNewest && addMessageToList) { return newMessages.concat(newMessage); } else if (messageIsNewest) { return newMessages; } // find the closest index to push the new message var insertionIndex = findIndexInSortedArray({ needle: newMessage, sortedArray: newMessages, sortDirection: 'ascending', // eslint-disable-next-line @typescript-eslint/no-non-null-assertion selectValueToCompare: function selectValueToCompare(m) { return m[sortBy].getTime(); }, selectKey: function selectKey(m) { return m.id; } }); // message already exists and not filtered with timestampChanged, update and return if (!timestampChanged && newMessage.id && newMessages[insertionIndex] && newMessage.id === newMessages[insertionIndex].id) { newMessages[insertionIndex] = newMessage; return newMessages; } // do not add updated or deleted messages to the list if they already exist or come with a timestamp change if (addMessageToList) { newMessages.splice(insertionIndex, 0, newMessage); } return newMessages; } function maybeGetReactionGroupsFallback(groups, counts, scores) { if (groups) { return groups; } if (counts && scores) { var fallback = {}; for (var _i2 = 0, _Object$keys = Object.keys(counts); _i2 < _Object$keys.length; _i2++) { var type = _Object$keys[_i2]; fallback[type] = { count: counts[type], sum_scores: scores[type] }; } return fallback; } return null; } // eslint-disable-next-line @typescript-eslint/no-explicit-any // works exactly the same as lodash.debounce // eslint-disable-next-line @typescript-eslint/no-explicit-any var debounce = function debounce(fn) { var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref2$leading = _ref2.leading, leading = _ref2$leading === void 0 ? false : _ref2$leading, _ref2$trailing = _ref2.trailing, trailing = _ref2$trailing === void 0 ? true : _ref2$trailing; var runningTimeout = null; var argsForTrailingExecution = null; var lastResult; var debouncedFn = function debouncedFn() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (runningTimeout) { clearTimeout(runningTimeout); } else if (leading) { lastResult = fn.apply(void 0, args); } if (trailing) argsForTrailingExecution = args; var timeoutHandler = function timeoutHandler() { if (argsForTrailingExecution) { lastResult = fn.apply(void 0, _toConsumableArray(argsForTrailingExecution)); argsForTrailingExecution = null; } runningTimeout = null; }; runningTimeout = setTimeout(timeoutHandler, timeout); return lastResult; }; debouncedFn.cancel = function () { if (runningTimeout) clearTimeout(runningTimeout); }; debouncedFn.flush = function () { if (runningTimeout) { clearTimeout(runningTimeout); runningTimeout = null; if (argsForTrailingExecution) { lastResult = fn.apply(void 0, _toConsumableArray(argsForTrailingExecution)); } } return lastResult; }; return debouncedFn; }; // works exactly the same as lodash.throttle var throttle = function throttle(fn) { var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 200; var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref3$leading = _ref3.leading, leading = _ref3$leading === void 0 ? true : _ref3$leading, _ref3$trailing = _ref3.trailing, trailing = _ref3$trailing === void 0 ? false : _ref3$trailing; var runningTimeout = null; var storedArgs = null; return function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } if (runningTimeout) { if (trailing) storedArgs = args; return; } if (leading) fn.apply(void 0, args); var timeoutHandler = function timeoutHandler() { if (storedArgs) { fn.apply(void 0, _toConsumableArray(storedArgs)); storedArgs = null; runningTimeout = setTimeout(timeoutHandler, timeout); return; } runningTimeout = null; }; runningTimeout = setTimeout(timeoutHandler, timeout); }; }; var get = function get(obj, path) { return path.split('.').reduce(function (acc, key) { if (acc && _typeof(acc) === 'object' && key in acc) { return acc[key]; } return undefined; }, obj); }; // works exactly the same as lodash.uniqBy var uniqBy = function uniqBy(array, iteratee) { if (!Array.isArray(array)) return []; var seen = new Set(); return array.filter(function (item) { var key = typeof iteratee === 'function' ? iteratee(item) : get(item, iteratee); if (seen.has(key)) return false; seen.add(key); return true; }); }; function binarySearchByDateEqualOrNearestGreater(array, targetDate) { var left = 0; var right = array.length - 1; while (left <= right) { var mid = Math.floor((left + right) / 2); var midCreatedAt = array[mid].created_at; if (!midCreatedAt) { left += 1; continue; } var midDate = new Date(midCreatedAt); if (midDate.getTime() === targetDate.getTime()) { return mid; } else if (midDate.getTime() < targetDate.getTime()) { left = mid + 1; } else { right = mid - 1; } } return left; } var messagePaginationCreatedAtAround = function messagePaginationCreatedAtAround(_ref4) { var parentSet = _ref4.parentSet, requestedPageSize = _ref4.requestedPageSize, returnedPage = _ref4.returnedPage, messagePaginationOptions = _ref4.messagePaginationOptions; var newPagination = _objectSpread$e({}, parentSet.pagination); if (!(messagePaginationOptions !== null && messagePaginationOptions !== void 0 && messagePaginationOptions.created_at_around)) return newPagination; var hasPrev; var hasNext; var updateHasPrev; var updateHasNext; var createdAtAroundDate = new Date(messagePaginationOptions.created_at_around); var _ref5 = [returnedPage[0], returnedPage.slice(-1)[0]], firstPageMsg = _ref5[0], lastPageMsg = _ref5[1]; // expect ASC order (from oldest to newest) var wholePageHasNewerMessages = !!(firstPageMsg !== null && firstPageMsg !== void 0 && firstPageMsg.created_at) && new Date(firstPageMsg.created_at) > createdAtAroundDate; var wholePageHasOlderMessages = !!(lastPageMsg !== null && lastPageMsg !== void 0 && lastPageMsg.created_at) && new Date(lastPageMsg.created_at) < createdAtAroundDate; var requestedPageSizeNotMet = requestedPageSize > parentSet.messages.length && requestedPageSize > returnedPage.length; var noMoreMessages = (requestedPageSize > parentSet.messages.length || parentSet.messages.length >= returnedPage.length) && requestedPageSize > returnedPage.length; if (wholePageHasNewerMessages) { hasPrev = false; updateHasPrev = true; if (requestedPageSizeNotMet) { hasNext = false; updateHasNext = true; } } else if (wholePageHasOlderMessages) { hasNext = false; updateHasNext = true; if (requestedPageSizeNotMet) { hasPrev = false; updateHasPrev = true; } } else if (noMoreMessages) { hasNext = hasPrev = false; updateHasPrev = updateHasNext = true; } else { var _parentSet$messages$, _parentSet$messages$s; var firstPageMsgIsFirstInSet = (firstPageMsg === null || firstPageMsg === void 0 ? void 0 : firstPageMsg.id) && firstPageMsg.id === ((_parentSet$messages$ = parentSet.messages[0]) === null || _parentSet$messages$ === void 0 ? void 0 : _parentSet$messages$.id), lastPageMsgIsLastInSet = (lastPageMsg === null || lastPageMsg === void 0 ? void 0 : lastPageMsg.id) && lastPageMsg.id === ((_parentSet$messages$s = parentSet.messages.slice(-1)[0]) === null || _parentSet$messages$s === void 0 ? void 0 : _parentSet$messages$s.id); updateHasPrev = firstPageMsgIsFirstInSet; updateHasNext = lastPageMsgIsLastInSet; var midPointByCount = Math.floor(returnedPage.length / 2); var midPointByCreationDate = binarySearchByDateEqualOrNearestGreater(returnedPage, createdAtAroundDate); if (midPointByCreationDate !== -1) { hasPrev = midPointByCount <= midPointByCreationDate; hasNext = midPointByCount >= midPointByCreationDate; } } if (updateHasPrev && typeof hasPrev !== 'undefined') newPagination.hasPrev = hasPrev; if (updateHasNext && typeof hasNext !== 'undefined') newPagination.hasNext = hasNext; return newPagination; }; var messagePaginationIdAround = function messagePaginationIdAround(_ref6) { var _parentSet$messages$2, _parentSet$messages$s2; var parentSet = _ref6.parentSet, requestedPageSize = _ref6.requestedPageSize, returnedPage = _ref6.returnedPage, messagePaginationOptions = _ref6.messagePaginationOptions; var newPagination = _objectSpread$e({}, parentSet.pagination); var _ref7 = messagePaginationOptions || {}, id_around = _ref7.id_around; if (!id_around) return newPagination; var hasPrev; var hasNext; var _ref8 = [returnedPage[0], returnedPage.slice(-1)[0]], firstPageMsg = _ref8[0], lastPageMsg = _ref8[1]; var firstPageMsgIsFirstInSet = (firstPageMsg === null || firstPageMsg === void 0 ? void 0 : firstPageMsg.id) === ((_parentSet$messages$2 = parentSet.messages[0]) === null || _parentSet$messages$2 === void 0 ? void 0 : _parentSet$messages$2.id), lastPageMsgIsLastInSet = (lastPageMsg === null || lastPageMsg === void 0 ? void 0 : lastPageMsg.id) === ((_parentSet$messages$s2 = parentSet.messages.slice(-1)[0]) === null || _parentSet$messages$s2 === void 0 ? void 0 : _parentSet$messages$s2.id); var updateHasPrev = firstPageMsgIsFirstInSet; var updateHasNext = lastPageMsgIsLastInSet; var midPoint = Math.floor(returnedPage.length / 2); var noMoreMessages = (requestedPageSize > parentSet.messages.length || parentSet.messages.length >= returnedPage.length) && requestedPageSize > returnedPage.length; if (noMoreMessages) { hasNext = hasPrev = false; updateHasPrev = updateHasNext = true; } else if (!returnedPage[midPoint]) { return newPagination; } else if (returnedPage[midPoint].id === id_around) { hasPrev = hasNext = true; } else { var targetMsg; var halves = [returnedPage.slice(0, midPoint), returnedPage.slice(midPoint)]; hasPrev = hasNext = true; for (var i = 0; i < halves.length; i++) { targetMsg = halves[i].find(function (message) { return message.id === id_around; }); if (targetMsg && i === 0) { hasPrev = false; } if (targetMsg && i === 1) { hasNext = false; } } } if (updateHasPrev && typeof hasPrev !== 'undefined') newPagination.hasPrev = hasPrev; if (updateHasNext && typeof hasNext !== 'undefined') newPagination.hasNext = hasNext; return newPagination; }; var messagePaginationLinear = function messagePaginationLinear(_ref9) { var _parentSet$messages$3, _parentSet$messages$s3; var parentSet = _ref9.parentSet, requestedPageSize = _ref9.requestedPageSize, returnedPage = _ref9.returnedPage, messagePaginationOptions = _ref9.messagePaginationOptions; var newPagination = _objectSpread$e({}, parentSet.pagination); var hasPrev; var hasNext; var _ref10 = [returnedPage[0], returnedPage.slice(-1)[0]], firstPageMsg = _ref10[0], lastPageMsg = _ref10[1]; var firstPageMsgIsFirstInSet = (firstPageMsg === null || firstPageMsg === void 0 ? void 0 : firstPageMsg.id) && firstPageMsg.id === ((_parentSet$messages$3 = parentSet.messages[0]) === null || _parentSet$messages$3 === void 0 ? void 0 : _parentSet$messages$3.id), lastPageMsgIsLastInSet = (lastPageMsg === null || lastPageMsg === void 0 ? void 0 : lastPageMsg.id) && lastPageMsg.id === ((_parentSet$messages$s3 = parentSet.messages.slice(-1)[0]) === null || _parentSet$messages$s3 === void 0 ? void 0 : _parentSet$messages$s3.id); var queriedNextMessages = messagePaginationOptions && (messagePaginationOptions.created_at_after_or_equal || messagePaginationOptions.created_at_after || messagePaginationOptions.id_gt || messagePaginationOptions.id_gte); var queriedPrevMessages = typeof messagePaginationOptions === 'undefined' ? true : messagePaginationOptions.created_at_before_or_equal || messagePaginationOptions.created_at_before || messagePaginationOptions.id_lt || messagePaginationOptions.id_lte || messagePaginationOptions.offset; var containsUnrecognizedOptionsOnly = !queriedNextMessages && !queriedPrevMessages && !(messagePaginationOptions !== null && messagePaginationOptions !== void 0 && messagePaginationOptions.id_around) && !(messagePaginationOptions !== null && messagePaginationOptions !== void 0 && messagePaginationOptions.created_at_around); var hasMore = returnedPage.length >= requestedPageSize; if (typeof queriedPrevMessages !== 'undefined' || containsUnrecognizedOptionsOnly) { hasPrev = hasMore; } if (typeof queriedNextMessages !== 'undefined') { hasNext = hasMore; } var returnedPageIsEmpty = returnedPage.length === 0; if ((firstPageMsgIsFirstInSet || returnedPageIsEmpty) && typeof hasPrev !== 'undefined') newPagination.hasPrev = hasPrev; if ((lastPageMsgIsLastInSet || returnedPageIsEmpty) && typeof hasNext !== 'undefined') newPagination.hasNext = hasNext; return newPagination; }; var messageSetPagination = function messageSetPagination(params) { var _params$messagePagina, _params$messagePagina2; if (params.parentSet.messages.length < params.returnedPage.length) { var _params$logger; (_params$logger = params.logger) === null || _params$logger === void 0 ? void 0 : _params$logger.call(params, 'error', 'Corrupted message set state: parent set size < returned page size'); return params.parentSet.pagination; } if ((_params$messagePagina = params.messagePaginationOptions) !== null && _params$messagePagina !== void 0 && _params$messagePagina.created_at_around) { return messagePaginationCreatedAtAround(params); } else if ((_params$messagePagina2 = params.messagePaginationOptions) !== null && _params$messagePagina2 !== void 0 && _params$messagePagina2.id_around) { return messagePaginationIdAround(params); } else { return messagePaginationLinear(params); } }; /** * A utility object used to prevent duplicate invocation of channel.watch() to be triggered when * 'notification.message_new' and 'notification.added_to_channel' events arrive at the same time. */ var WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL = {}; /** * Calls channel.watch() if it was not already recently called. Waits for watch promise to resolve even if it was invoked previously. * If the channel is not passed as a property, it will get it either by its channel.cid or by its members list and do the same. * @param client * @param members * @param options * @param type * @param id * @param channel */ var getAndWatchChannel = /*#__PURE__*/function () { var _ref12 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref11) { var channel, client, id, members, options, type, channelToWatch, originalCid, queryPromise; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: channel = _ref11.channel, client = _ref11.client, id = _ref11.id, members = _ref11.members, options = _ref11.options, type = _ref11.type; if (!(!channel && !type)) { _context.next = 3; break; } throw new Error('Channel or channel type have to be provided to query a channel.'); case 3: // unfortunately typescript is not able to infer that if (!channel && !type) === false, then channel or type has to be truthy // eslint-disable-next-line @typescript-eslint/no-non-null-assertion channelToWatch = channel || client.channel(type, id, { members: members }); // need to keep as with call to channel.watch the id can be changed from undefined to an actual ID generated server-side originalCid = channelToWatch.id ? channelToWatch.cid : members && members.length ? generateChannelTempCid(channelToWatch.type, members) : undefined; if (originalCid) { _context.next = 7; break; } throw new Error('Channel ID or channel members array have to be provided to query a channel.'); case 7: queryPromise = WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[originalCid]; if (!queryPromise) { _context.next = 13; break; } _context.next = 11; return queryPromise; case 11: _context.next = 20; break; case 13: _context.prev = 13; WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[originalCid] = channelToWatch.watch(options); _context.next = 17; return WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[originalCid]; case 17: _context.prev = 17; delete WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[originalCid]; return _context.finish(17); case 20: return _context.abrupt("return", channelToWatch); case 21: case "end": return _context.stop(); } } }, _callee, null, [[13,, 17, 20]]); })); return function getAndWatchChannel(_x) { return _ref12.apply(this, arguments); }; }(); /** * Generates a temporary channel.cid for channels created without ID, as they need to be referenced * by an identifier until the back-end generates the final ID. The cid is generated by its member IDs * which are sorted and can be recreated the same every time given the same arguments. * @param channelType * @param members */ var generateChannelTempCid = function generateChannelTempCid(channelType, members) { if (!members) return; var membersStr = _toConsumableArray(members).sort().join(','); if (!membersStr) return; return "".concat(channelType, ":!members-").concat(membersStr); }; /** * Checks if a channel is pinned or not. Will return true only if channel.state.membership.pinned_at exists. * @param channel */ var isChannelPinned = function isChannelPinned(channel) { if (!channel) return false; var member = channel.state.membership; return !!(member !== null && member !== void 0 && member.pinned_at); }; /** * Checks if a channel is archived or not. Will return true only if channel.state.membership.archived_at exists. * @param channel */ var isChannelArchived = function isChannelArchived(channel) { if (!channel) return false; var member = channel.state.membership; return !!(member !== null && member !== void 0 && member.archived_at); }; /** * A utility that tells us whether we should consider archived channels or not based * on filters. Will return true only if filters.archived exists and is a boolean value. * @param filters */ var shouldConsiderArchivedChannels = function shouldConsiderArchivedChannels(filters) { if (!filters) return false; return typeof filters.archived === 'boolean'; }; /** * Extracts the value of the sort parameter at a given index, for a targeted key. Can * handle both array and object versions of sort. Will return null if the index/key * combination does not exist. * @param atIndex - the index at which we'll examine the sort value, if it's an array one * @param sort - the sort value - both array and object notations are accepted * @param targetKey - the target key which needs to exist for the sort at a certain index */ var extractSortValue = function extractSortValue(_ref13) { var _option$targetKey, _option; var atIndex = _ref13.atIndex, sort = _ref13.sort, targetKey = _ref13.targetKey; if (!sort) return null; var option = null; if (Array.isArray(sort)) { var _sort$atIndex; option = (_sort$atIndex = sort[atIndex]) !== null && _sort$atIndex !== void 0 ? _sort$atIndex : null; } else { var index = 0; for (var _key3 in sort) { if (index !== atIndex) { index++; continue; } if (_key3 !== targetKey) { return null; } option = sort; break; } } return (_option$targetKey = (_option = option) === null || _option === void 0 ? void 0 : _option[targetKey]) !== null && _option$targetKey !== void 0 ? _option$targetKey : null; }; /** * Returns true only if `{ pinned_at: -1 }` or `{ pinned_at: 1 }` option is first within the `sort` array. */ var shouldConsiderPinnedChannels = function shouldConsiderPinnedChannels(sort) { var value = findPinnedAtSortOrder({ sort: sort }); if (typeof value !== 'number') return false; return Math.abs(value) === 1; }; /** * Checks whether the sort value of type object contains a pinned_at value or if * an array sort value type has the first value be an object containing pinned_at. * @param sort */ var findPinnedAtSortOrder = function findPinnedAtSortOrder(_ref14) { var sort = _ref14.sort; return extractSortValue({ atIndex: 0, sort: sort, targetKey: 'pinned_at' }); }; /** * Finds the index of the last consecutively pinned channel, starting from the start of the * array. Will not consider any pinned channels after the contiguous subsequence at the * start of the array. * @param channels */ var findLastPinnedChannelIndex = function findLastPinnedChannelIndex(_ref15) { var channels = _ref15.channels; var lastPinnedChannelIndex = null; var _iterator2 = _createForOfIteratorHelper$5(channels), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var channel = _step2.value; if (!isChannelPinned(channel)) break; if (typeof lastPinnedChannelIndex === 'number') { lastPinnedChannelIndex++; } else { lastPinnedChannelIndex = 0; } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } return lastPinnedChannelIndex; }; /** * A utility used to move a channel towards the beginning of a list of channels (promote it to a higher position). It * considers pinned channels in the process if needed and makes sure to only update the list reference if the list * should actually change. It will try to move the channel as high as it can within the list. * @param channels - the list of channels we want to modify * @param channelToMove - the channel we want to promote * @param channelToMoveIndexWithinChannels - optionally, the index of the channel we want to move if we know it (will skip a manual check) * @param sort - the sort value used to check for pinned channels */ var promoteChannel = function promoteChannel(_ref16) { var channels = _ref16.channels, channelToMove = _ref16.channelToMove, channelToMoveIndexWithinChannels = _ref16.channelToMoveIndexWithinChannels, sort = _ref16.sort; // get index of channel to move up var targetChannelIndex = channelToMoveIndexWithinChannels !== null && channelToMoveIndexWithinChannels !== void 0 ? channelToMoveIndexWithinChannels : channels.findIndex(function (channel) { return channel.cid === channelToMove.cid; }); var targetChannelExistsWithinList = targetChannelIndex >= 0; var targetChannelAlreadyAtTheTop = targetChannelIndex === 0; // pinned channels should not move within the list based on recent activity, channels which // receive messages and are not pinned should move upwards but only under the last pinned channel // in the list var considerPinnedChannels = shouldConsiderPinnedChannels(sort); var isTargetChannelPinned = isChannelPinned(channelToMove); if (targetChannelAlreadyAtTheTop || considerPinnedChannels && isTargetChannelPinned) { return channels; } var newChannels = _toConsumableArray(channels); // target channel index is known, remove it from the list if (targetChannelExistsWithinList) { newChannels.splice(targetChannelIndex, 1); } // as position of pinned channels has to stay unchanged, we need to // find last pinned channel in the list to move the target channel after var lastPinnedChannelIndex = null; if (considerPinnedChannels) { lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels }); } // re-insert it at the new place (to specific index if pinned channels are considered) newChannels.splice(typeof lastPinnedChannelIndex === 'number' ? lastPinnedChannelIndex + 1 : 0, 0, channelToMove); return newChannels; }; var DEFAULT_QUERY_CHANNELS_MESSAGE_LIST_PAGE_SIZE = 25; var DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE = 100; var DEFAULT_MESSAGE_SET_PAGINATION = { hasNext: false, hasPrev: false }; function ownKeys$d(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread$d(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$d(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$d(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } /** * ChannelState - A container class for the ch