stream-chat
Version:
JS SDK for the Stream Chat API
1,312 lines (1,088 loc) • 706 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var base64Js = require('base64-js');
var _asyncToGenerator = require('@babel/runtime/helpers/asyncToGenerator');
var _classCallCheck = require('@babel/runtime/helpers/classCallCheck');
var _createClass = require('@babel/runtime/helpers/createClass');
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var _regeneratorRuntime = require('@babel/runtime/regenerator');
var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
var _extends = require('@babel/runtime/helpers/extends');
var _typeof = require('@babel/runtime/helpers/typeof');
var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
var _toConsumableArray = require('@babel/runtime/helpers/toConsumableArray');
var axios = require('axios');
var FormData = require('form-data');
var WebSocket = require('isomorphic-ws');
var _assertThisInitialized = require('@babel/runtime/helpers/assertThisInitialized');
var _inherits = require('@babel/runtime/helpers/inherits');
var _possibleConstructorReturn = require('@babel/runtime/helpers/possibleConstructorReturn');
var _getPrototypeOf = require('@babel/runtime/helpers/getPrototypeOf');
var _wrapNativeSuper = require('@babel/runtime/helpers/wrapNativeSuper');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var _asyncToGenerator__default = /*#__PURE__*/_interopDefaultLegacy(_asyncToGenerator);
var _classCallCheck__default = /*#__PURE__*/_interopDefaultLegacy(_classCallCheck);
var _createClass__default = /*#__PURE__*/_interopDefaultLegacy(_createClass);
var _defineProperty__default = /*#__PURE__*/_interopDefaultLegacy(_defineProperty);
var _regeneratorRuntime__default = /*#__PURE__*/_interopDefaultLegacy(_regeneratorRuntime);
var _slicedToArray__default = /*#__PURE__*/_interopDefaultLegacy(_slicedToArray);
var _extends__default = /*#__PURE__*/_interopDefaultLegacy(_extends);
var _typeof__default = /*#__PURE__*/_interopDefaultLegacy(_typeof);
var _objectWithoutProperties__default = /*#__PURE__*/_interopDefaultLegacy(_objectWithoutProperties);
var _toConsumableArray__default = /*#__PURE__*/_interopDefaultLegacy(_toConsumableArray);
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
var FormData__default = /*#__PURE__*/_interopDefaultLegacy(FormData);
var WebSocket__default = /*#__PURE__*/_interopDefaultLegacy(WebSocket);
var _assertThisInitialized__default = /*#__PURE__*/_interopDefaultLegacy(_assertThisInitialized);
var _inherits__default = /*#__PURE__*/_interopDefaultLegacy(_inherits);
var _possibleConstructorReturn__default = /*#__PURE__*/_interopDefaultLegacy(_possibleConstructorReturn);
var _getPrototypeOf__default = /*#__PURE__*/_interopDefaultLegacy(_getPrototypeOf);
var _wrapNativeSuper__default = /*#__PURE__*/_interopDefaultLegacy(_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 base64Js.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__default['default'](this, Campaign);
_defineProperty__default['default'](this, "id", void 0);
_defineProperty__default['default'](this, "data", void 0);
_defineProperty__default['default'](this, "client", void 0);
this.client = client;
this.id = id;
this.data = data;
}
_createClass__default['default'](Campaign, [{
key: "create",
value: function () {
var _create = _asyncToGenerator__default['default']( /*#__PURE__*/_regeneratorRuntime__default['default'].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__default['default'].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__default['default']( /*#__PURE__*/_regeneratorRuntime__default['default'].mark(function _callee2(options) {
return _regeneratorRuntime__default['default'].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__default['default']( /*#__PURE__*/_regeneratorRuntime__default['default'].mark(function _callee3(data) {
return _regeneratorRuntime__default['default'].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__default['default']( /*#__PURE__*/_regeneratorRuntime__default['default'].mark(function _callee4() {
return _regeneratorRuntime__default['default'].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__default['default']( /*#__PURE__*/_regeneratorRuntime__default['default'].mark(function _callee5() {
return _regeneratorRuntime__default['default'].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__default['default']( /*#__PURE__*/_regeneratorRuntime__default['default'].mark(function _callee6(options) {
return _regeneratorRuntime__default['default'].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__default['default'](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__default['default'](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__default['default']();
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__default['default'](_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__default['default'](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__default['default'](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__default['default'](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__default['default'](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__default['default'](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__default['default'](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__default['default']( /*#__PURE__*/_regeneratorRuntime__default['default'].mark(function _callee(_ref11) {
var channel, client, id, members, options, type, channelToWatch, originalCid, queryPromise;
return _regeneratorRuntime__default['default'].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__default['default'](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 ===