known
to local Client, Client is invited
to or
* is joined
to this Channel
* @typedef {('unknown' | 'known' | 'invited' | 'joined')} Channel#Status
*/
/**
* The type of Channel (public
or private
).
* @typedef {('public' | 'private')} Channel#Type
*/
/**
* The User's Notification level for Channel, determines whether the currently logged-in User will receive
* pushes for events in this Channel. Can be either muted
or default
,
* where default
defers to global Service push configuration.
* @typedef {('default' | 'muted')} Channel#NotificationLevel
*/
function Channel(services, descriptor, sid) {
(0, _classCallCheck3.default)(this, Channel);
var _this = (0, _possibleConstructorReturn3.default)(this, (Channel.__proto__ || (0, _getPrototypeOf2.default)(Channel)).call(this));
var attributes = descriptor.attributes || {};
var createdBy = descriptor.createdBy;
var dateCreated = parseTime(descriptor.dateCreated);
var dateUpdated = parseTime(descriptor.dateUpdated);
var friendlyName = descriptor.name || descriptor.friendlyName || null;
var lastConsumedMessageIndex = (0, _isInteger2.default)(descriptor.lastConsumedMessageIndex) ? descriptor.lastConsumedMessageIndex : null;
var uniqueName = descriptor.uniqueName || null;
try {
(0, _stringify2.default)(attributes);
} catch (e) {
throw new Error('Attributes must be a valid JSON object.');
}
_this.services = services;
_this.sid = sid;
_this.entityName = descriptor.channel;
_this.state = {
uniqueName: uniqueName,
status: 'known',
type: descriptor.type,
attributes: attributes,
createdBy: createdBy,
dateCreated: dateCreated,
dateUpdated: dateUpdated,
friendlyName: friendlyName,
lastConsumedMessageIndex: lastConsumedMessageIndex
};
if (descriptor.notificationLevel) {
_this.state.notificationLevel = descriptor.notificationLevel;
}
_this.members = new _map2.default();
_this.membersEntity = new members_1.Members(_this, _this.services, _this.members);
_this.membersEntity.on('memberJoined', _this.emit.bind(_this, 'memberJoined'));
_this.membersEntity.on('memberLeft', _this.emit.bind(_this, 'memberLeft'));
_this.membersEntity.on('memberUpdated', function (args) {
return _this.emit('memberUpdated', args);
});
_this.messagesEntity = new messages_1.Messages(_this, services);
_this.messagesEntity.on('messageAdded', function (message) {
return _this._onMessageAdded(message);
});
_this.messagesEntity.on('messageUpdated', function (args) {
return _this.emit('messageUpdated', args);
});
_this.messagesEntity.on('messageRemoved', _this.emit.bind(_this, 'messageRemoved'));
return _this;
}
(0, _createClass3.default)(Channel, [{
key: "_subscribe",
/**
* The Channel's last message's information.
* @typedef {Object} Channel#LastMessage
* @property {Number} index - Message's index
* @property {Date} timestamp - Message's creation timestamp
*/
/**
* Load and Subscribe to this Channel and do not subscribe to its Members and Messages.
* This or _subscribeStreams will need to be called before any events on Channel will fire.
* @returns {Promise}
* @private
*/
value: function _subscribe() {
var _this2 = this;
if (this.entityPromise) {
return this.entityPromise;
}
return this.entityPromise = this.entityPromise || this.services.syncClient.document({ id: this.entityName, mode: 'open_existing' }).then(function (entity) {
_this2.entity = entity;
_this2.entity.on('updated', function (args) {
_this2._update(args.value);
});
_this2.entity.on('removed', function () {
return _this2.emit('removed', _this2);
});
_this2._update(_this2.entity.value);
return entity;
}).catch(function (err) {
_this2.entity = null;
_this2.entityPromise = null;
if (_this2.services.syncClient.connectionState != 'disconnected') {
log.error('Failed to get channel object', err);
}
log.debug('ERROR: Failed to get channel object', err);
throw err;
});
}
/**
* Load the attributes of this Channel and instantiate its Members and Messages.
* This or _subscribe will need to be called before any events on Channel will fire.
* This will need to be called before any events on Members or Messages will fire
* @returns {Promise}
* @private
*/
}, {
key: "_subscribeStreams",
value: function _subscribeStreams() {
return __awaiter(this, void 0, void 0, /*#__PURE__*/_regenerator2.default.mark(function _callee() {
var messagesObjectName, rosterObjectName;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
_context.next = 3;
return this._subscribe();
case 3:
log.trace('_subscribeStreams, this.entity.value=', this.entity.value);
messagesObjectName = this.entity.value.messages;
rosterObjectName = this.entity.value.roster;
_context.next = 8;
return _promise2.default.all([this.messagesEntity.subscribe(messagesObjectName), this.membersEntity.subscribe(rosterObjectName)]);
case 8:
_context.next = 15;
break;
case 10:
_context.prev = 10;
_context.t0 = _context["catch"](0);
if (this.services.syncClient.connectionState !== 'disconnected') {
log.error('Failed to subscribe on channel objects', this.sid, _context.t0);
}
log.debug('ERROR: Failed to subscribe on channel objects', this.sid, _context.t0);
throw _context.t0;
case 15:
case "end":
return _context.stop();
}
}
}, _callee, this, [[0, 10]]);
}));
}
/**
* Stop listening for and firing events on this Channel.
* @returns {Promise}
* @private
*/
}, {
key: "_unsubscribe",
value: function _unsubscribe() {
return __awaiter(this, void 0, void 0, /*#__PURE__*/_regenerator2.default.mark(function _callee2() {
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!(this.isPrivate && this.entity)) {
_context2.next = 5;
break;
}
_context2.next = 3;
return this.entity.close();
case 3:
this.entity = null;
this.entityPromise = null;
case 5:
return _context2.abrupt("return", _promise2.default.all([this.membersEntity.unsubscribe(), this.messagesEntity.unsubscribe()]));
case 6:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
}
/**
* Set channel status
* @private
*/
}, {
key: "_setStatus",
value: function _setStatus(status, source) {
var _this3 = this;
this.statusSource = source;
if (this.state.status === status) {
return;
}
this.state.status = status;
if (status === 'joined') {
this._subscribeStreams().catch(function (err) {
log.debug('ERROR while setting channel status ' + status, err);
if (_this3.services.syncClient.connectionState !== 'disconnected') {
throw err;
}
});
} else if (status === 'invited') {
this._subscribe().catch(function (err) {
log.debug('ERROR while setting channel status ' + status, err);
if (_this3.services.syncClient.connectionState !== 'disconnected') {
throw err;
}
});
} else if (this.entityPromise) {
this._unsubscribe().catch(function (err) {
log.debug('ERROR while setting channel status ' + status, err);
if (_this3.services.syncClient.connectionState !== 'disconnected') {
throw err;
}
});
}
}
/**
* If channel's status update source
* @private
* @return {Channels.DataSource}
*/
}, {
key: "_statusSource",
value: function _statusSource() {
return this.statusSource;
}
}, {
key: "_update",
/**
* Updates local channel object with new values
* @private
*/
value: function _update(update) {
log.trace('_update', update);
var updateReasons = [];
Channel.preprocessUpdate(update, this.sid);
for (var key in update) {
var localKey = fieldMappings[key];
if (!localKey) {
continue;
}
if (localKey === fieldMappings.status) {
if (update.status && update.status != 'unknown' && this.state.status !== filterStatus(update.status)) {
this.state.status = filterStatus(update.status);
updateReasons.push(localKey);
}
} else if (localKey === fieldMappings.attributes) {
if (!util_1.isDeepEqual(this.state.attributes, update.attributes)) {
this.state.attributes = update.attributes;
updateReasons.push(localKey);
}
} else if (localKey === fieldMappings.lastConsumedMessageIndex) {
if (!(typeof update.lastConsumedMessageIndex === 'undefined') && update.lastConsumedMessageIndex !== this.state.lastConsumedMessageIndex) {
this.state.lastConsumedMessageIndex = update.lastConsumedMessageIndex;
updateReasons.push(localKey);
}
} else if (localKey === fieldMappings.lastMessage) {
var updated = false;
if (this.state.lastMessage && !update.lastMessage) {
delete this.state.lastMessage;
updated = true;
} else {
if (!this.state.lastMessage) {
this.state.lastMessage = {};
}
if (update.lastMessage && typeof update.lastMessage.index !== 'undefined' && update.lastMessage.index !== this.state.lastMessage.index) {
this.state.lastMessage.index = update.lastMessage.index;
updated = true;
}
if (update.lastMessage && update.lastMessage.timestamp && (!this.state.lastMessage.timestamp || this.state.lastMessage.timestamp.getTime() !== update.lastMessage.timestamp.getTime())) {
this.state.lastMessage.timestamp = update.lastMessage.timestamp;
updated = true;
}
if (util_1.isDeepEqual(this.state.lastMessage, {})) {
delete this.state.lastMessage;
}
}
if (updated) {
updateReasons.push(localKey);
}
} else if (update[key] instanceof Date) {
if (!this.state[localKey] || this.state[localKey].getTime() !== update[key].getTime()) {
this.state[localKey] = update[key];
updateReasons.push(localKey);
}
} else if (this[localKey] !== update[key]) {
this.state[localKey] = update[key];
updateReasons.push(localKey);
}
}
if (updateReasons.length > 0) {
this.emit('updated', { channel: this, updateReasons: updateReasons });
}
}
/**
* @private
*/
}, {
key: "_onMessageAdded",
value: function _onMessageAdded(message) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(this.members.values()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var member = _step.value;
if (member.identity === message.author) {
member._endTyping();
break;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
this.emit('messageAdded', message);
}
/**
* Add a participant to the Channel by its Identity.
* @param {String} identity - Identity of the Client to add
* @returns {Promiseattributes
property will be empty for public channels until this function is called.
* @returns {Promise