@hsaadawy/ngx-chat
Version:
1,075 lines (1,051 loc) • 215 kB
JavaScript
import { __awaiter } from 'tslib';
import { EventEmitter, Component, Output, Input, HostListener, InjectionToken, Inject, ViewChild, Injectable, NgZone, ElementRef, Optional, ChangeDetectorRef, ViewChildren, ChangeDetectionStrategy, Directive, ComponentFactoryResolver, ViewContainerRef, NgModule } from '@angular/core';
import { BehaviorSubject, Subject, combineLatest, merge, of } from 'rxjs';
import { filter, debounceTime, first, map, takeUntil, delay as delay$1, distinctUntilChanged, share, timeout as timeout$1, mergeMap, catchError } from 'rxjs/operators';
import { xml, jid, client } from '@xmpp/client';
export { jid as parseJid } from '@xmpp/client';
import { HttpHeaders, HttpClient, HttpClientModule } from '@angular/common/http';
import { PlatformLocation, CommonModule } from '@angular/common';
import { Router } from '@angular/router';
import { TextFieldModule } from '@angular/cdk/text-field';
import { FormsModule } from '@angular/forms';
import { trigger, state, style, transition, animate } from '@angular/animations';
export { JID } from '@xmpp/jid';
class FileDropComponent {
constructor() {
this.fileUpload = new EventEmitter();
this.enabled = true;
this.isDropTarget = false;
}
onDragOver(event) {
if (this.enabled) {
event.preventDefault();
event.stopPropagation();
this.isDropTarget = true;
}
}
onDragLeave(event) {
event.preventDefault();
event.stopPropagation();
this.isDropTarget = false;
}
onDrop(event) {
return __awaiter(this, void 0, void 0, function* () {
if (this.enabled) {
event.preventDefault();
event.stopPropagation();
this.isDropTarget = false;
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < event.dataTransfer.items.length; i++) {
const dataTransferItem = event.dataTransfer.items[i];
if (dataTransferItem.kind === 'file') {
this.fileUpload.emit(dataTransferItem.getAsFile());
}
}
}
});
}
}
FileDropComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-chat-filedrop',
template: "<div>\r\n <div class=\"drop-message\"\r\n [class.drop-message--visible]=\"isDropTarget\">\r\n {{dropMessage}}\r\n </div>\r\n <div>\r\n <ng-content></ng-content>\r\n </div>\r\n</div>\r\n",
styles: [".drop-message{pointer-events:none;display:none}.drop-message--visible{position:absolute;top:0;bottom:0;left:0;right:0;display:flex;justify-content:center;align-content:center;flex-direction:column;text-align:center;font-size:1.5em;z-index:999;background-color:#fff9;padding:1em}\n"]
},] }
];
FileDropComponent.propDecorators = {
fileUpload: [{ type: Output }],
dropMessage: [{ type: Input }],
enabled: [{ type: Input }],
onDragOver: [{ type: HostListener, args: ['dragover', ['$event'],] }, { type: HostListener, args: ['dragenter', ['$event'],] }],
onDragLeave: [{ type: HostListener, args: ['dragleave', ['$event'],] }, { type: HostListener, args: ['dragexit', ['$event'],] }],
onDrop: [{ type: HostListener, args: ['drop', ['$event'],] }]
};
class ReplyMessageEvent {
constructor() {
this.replyMessageEmitter$ = new EventEmitter();
}
changeReplyMessage(replyMessage) {
debugger;
this.replyMessageEmitter$.emit(replyMessage);
}
}
/**
* The chat service token gives you access to the main chat api and is implemented by default with an XMPP adapter,
* you can always reuse the api and ui with a new service implementing the ChatService interface and providing the
* said implementation with the token
*/
const CHAT_SERVICE_TOKEN = new InjectionToken('ngxChatService');
class ChatMessageInputComponent {
constructor(chatService, replyMessageEvent) {
this.chatService = chatService;
this.replyMessageEvent = replyMessageEvent;
this.messageSent = new EventEmitter();
this.message = "";
this.messageItem = "";
debugger;
this.replyMessageEvent.replyMessageEmitter$.subscribe((item) => {
// this.message = item
this.messageItem = item;
});
}
ngOnInit() {
}
onSendMessage($event) {
if ($event) {
$event.preventDefault();
}
if (this.Reply != '') {
this.chatService.sendMessage(this.recipient, `<div class="messageItem">${this.Reply}</div>` + this.message);
this.Reply = '';
}
else {
this.chatService.sendMessage(this.recipient, this.message);
}
this.message = "";
// this.messageSent.emit();
}
focus() {
this.chatInput.nativeElement.focus();
}
delete() {
debugger;
this.Reply = '';
}
}
ChatMessageInputComponent.decorators = [
{ type: Component, args: [{
selector: "ngx-chat-message-input",
template: "<div *ngIf=\"Reply!=''\" style=\" background: #8e89896b;\r\ncolor: #fff;\r\nposition: relative;\r\ntop: -9px;\r\nwidth: 105%;\r\nleft: -3%;\r\npadding: 4px 6px;\r\nborder-radius: 4px 4px 0 0;\">\r\n\r\n <div [innerHTML]=\"Reply\"></div>\r\n <i class=\"fas fa-times\" style=\"font-family: 'Font Awesome 5 Pro' !important;float: right;padding: 0 5px; position: absolute;\r\n right: 0;\r\n top: 4px;\" (click)=\"delete()\" aria-hidden=\"true\"></i>\r\n\r\n</div>\r\n\r\n<textarea class=\"chat-input\" #chatInput [(ngModel)]=\"message\" (keydown.enter)=\"onSendMessage($event)\"\r\n cdkTextareaAutosize cdkAutosizeMinRows=\"1\" cdkAutosizeMaxRows=\"5\"\r\n placeholder=\"{{chatService.translations.placeholder}}\"></textarea>\r\n",
styles: ["@keyframes ngx-chat-message-in{0%{transform:translate(50px);opacity:0}to{transform:none;opacity:1}}@keyframes ngx-chat-message-out{0%{transform:translate(-50px);opacity:0}to{transform:none;opacity:1}}.messageItem{background-color:red;width:100%;border-radius:2px}*{box-sizing:border-box;margin:0;padding:0;font-family:\"Helvetica\",\"Arial\",serif}.chat-input{border:none;width:100%;font-size:1em;padding:0;display:block;resize:none;overflow-x:hidden;outline:none}\n"]
},] }
];
ChatMessageInputComponent.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [CHAT_SERVICE_TOKEN,] }] },
{ type: ReplyMessageEvent, decorators: [{ type: Inject, args: [ReplyMessageEvent,] }] }
];
ChatMessageInputComponent.propDecorators = {
recipient: [{ type: Input }],
Reply: [{ type: Input }],
messageSent: [{ type: Output }],
chatInput: [{ type: ViewChild, args: ["chatInput",] }]
};
var MessageState;
(function (MessageState) {
/**
* Not yet sent
*/
MessageState["SENDING"] = "sending";
/**
* Sent, but neither received nor seen by the recipient
*/
MessageState["SENT"] = "sent";
/**
* The recipient client has received the message but the recipient has not seen it yet
*/
MessageState["RECIPIENT_RECEIVED"] = "recipientReceived";
/**
* The message has been seen by the recipient
*/
MessageState["RECIPIENT_SEEN"] = "recipientSeen";
})(MessageState || (MessageState = {}));
var Direction;
(function (Direction) {
Direction["in"] = "in";
Direction["out"] = "out";
})(Direction || (Direction = {}));
class AbstractXmppPlugin {
onBeforeOnline() {
return Promise.resolve();
}
onOffline() {
}
afterSendMessage(message, messageStanza) {
return;
}
beforeSendMessage(messageStanza, message) {
return;
}
handleStanza(stanza) {
return false;
}
afterReceiveMessage(message, messageStanza, messageReceivedEvent) {
return;
}
}
/**
* XEP-0191: Blocking Command
* https://xmpp.org/extensions/xep-0191.html
*/
class BlockPlugin extends AbstractXmppPlugin {
constructor(xmppChatAdapter, serviceDiscoveryPlugin) {
super();
this.xmppChatAdapter = xmppChatAdapter;
this.serviceDiscoveryPlugin = serviceDiscoveryPlugin;
this.supportsBlock$ = new BehaviorSubject('unknown');
}
onBeforeOnline() {
return __awaiter(this, void 0, void 0, function* () {
const supportsBlock = yield this.determineSupportForBlock();
this.supportsBlock$.next(supportsBlock);
if (supportsBlock) {
yield this.requestBlockedJids();
}
});
}
determineSupportForBlock() {
return __awaiter(this, void 0, void 0, function* () {
try {
return yield this.serviceDiscoveryPlugin.supportsFeature(this.xmppChatAdapter.chatConnectionService.userJid.domain, 'urn:xmpp:blocking');
}
catch (e) {
return false;
}
});
}
onOffline() {
this.supportsBlock$.next('unknown');
this.xmppChatAdapter.blockedContactIds$.next(new Set());
}
blockJid(jid) {
return this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'set' }, xml('block', { xmlns: 'urn:xmpp:blocking' }, xml('item', { jid }))));
}
unblockJid(jid) {
return this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'set' }, xml('unblock', { xmlns: 'urn:xmpp:blocking' }, xml('item', { jid }))));
}
requestBlockedJids() {
return __awaiter(this, void 0, void 0, function* () {
const blockListResponse = yield this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'get' }, xml('blocklist', { xmlns: 'urn:xmpp:blocking' })));
const blockedJids = blockListResponse
.getChild('blocklist')
.getChildren('item')
.map(e => e.attrs.jid);
this.xmppChatAdapter.blockedContactIds$.next(new Set(blockedJids));
});
}
handleStanza(stanza) {
var _a;
const { from } = stanza.attrs;
if (from && from === ((_a = this.xmppChatAdapter.chatConnectionService.userJid) === null || _a === void 0 ? void 0 : _a.bare().toString())) {
const blockPush = stanza.getChild('block', 'urn:xmpp:blocking');
const unblockPush = stanza.getChild('unblock', 'urn:xmpp:blocking');
const blockList = this.xmppChatAdapter.blockedContactIds$.getValue();
if (blockPush) {
blockPush.getChildren('item')
.map(e => e.attrs.jid)
.forEach(jid => blockList.add(jid));
this.xmppChatAdapter.blockedContactIds$.next(blockList);
return true;
}
else if (unblockPush) {
const jidsToUnblock = unblockPush.getChildren('item').map(e => e.attrs.jid);
if (jidsToUnblock.length === 0) {
// unblock everyone
blockList.clear();
}
else {
// unblock individually
jidsToUnblock.forEach(jid => blockList.delete(jid));
}
this.xmppChatAdapter.blockedContactIds$.next(blockList);
return true;
}
}
return false;
}
}
class AbstractStanzaBuilder {
}
class IqResponseError extends Error {
constructor(errorStanza) {
super(IqResponseError.extractErrorTextFromErrorResponse(errorStanza, IqResponseError.extractErrorDataFromErrorResponse(errorStanza)));
this.errorStanza = errorStanza;
const { code, type, condition } = IqResponseError.extractErrorDataFromErrorResponse(errorStanza);
this.errorCode = code;
this.errorType = type;
this.errorCondition = condition;
}
static extractErrorDataFromErrorResponse(stanza) {
var _a;
const errorElement = stanza.getChild('error');
const errorCode = Number(errorElement === null || errorElement === void 0 ? void 0 : errorElement.attrs.code) || undefined;
const errorType = errorElement === null || errorElement === void 0 ? void 0 : errorElement.attrs.type;
const errorCondition = (_a = errorElement === null || errorElement === void 0 ? void 0 : errorElement.children.filter(childElement => childElement.getName() !== 'text' &&
childElement.attrs.xmlns === IqResponseError.ERROR_ELEMENT_NS)[0]) === null || _a === void 0 ? void 0 : _a.getName();
return {
code: errorCode,
type: errorType,
condition: errorCondition,
};
}
static extractErrorTextFromErrorResponse(stanza, { code, type, condition }) {
var _a;
const additionalData = [
`errorCode: ${code !== null && code !== void 0 ? code : '[unknown]'}`,
`errorType: ${type !== null && type !== void 0 ? type : '[unknown]'}`,
`errorCondition: ${condition !== null && condition !== void 0 ? condition : '[unknown]'}`
].join(', ');
const errorText = ((_a = stanza.getChild('error')) === null || _a === void 0 ? void 0 : _a.getChildText('text', IqResponseError.ERROR_ELEMENT_NS)) || 'Unknown error';
return `IqResponseError: ${errorText}${additionalData ? ` (${additionalData})` : ''}`;
}
}
IqResponseError.ERROR_ELEMENT_NS = 'urn:ietf:params:xml:ns:xmpp-stanzas';
const PUBSUB_EVENT_XMLNS = 'http://jabber.org/protocol/pubsub#event';
class PublishStanzaBuilder extends AbstractStanzaBuilder {
constructor(options) {
super();
this.publishOptions = {
persistItems: false,
};
if (options) {
this.publishOptions = Object.assign(Object.assign({}, this.publishOptions), options);
}
}
toStanza() {
const { node, id, persistItems } = this.publishOptions;
// necessary as a 'event-only' publish is currently broken in ejabberd, see
// https://github.com/processone/ejabberd/issues/2799
const data = this.publishOptions.data || xml('data');
return xml('iq', { type: 'set' }, xml('pubsub', { xmlns: 'http://jabber.org/protocol/pubsub' }, xml('publish', { node }, xml('item', { id }, data)), xml('publish-options', {}, xml('x', { xmlns: 'jabber:x:data', type: 'submit' }, xml('field', { var: 'FORM_TYPE', type: 'hidden' }, xml('value', {}, 'http://jabber.org/protocol/pubsub#publish-options')), xml('field', { var: 'pubsub#persist_items' }, xml('value', {}, persistItems ? 1 : 0)), xml('field', { var: 'pubsub#access_model' }, xml('value', {}, 'whitelist'))))));
}
}
class RetrieveDataStanzaBuilder extends AbstractStanzaBuilder {
constructor(node) {
super();
this.node = node;
}
toStanza() {
return xml('iq', { type: 'get' }, xml('pubsub', { xmlns: 'http://jabber.org/protocol/pubsub' }, xml('items', { node: this.node })));
}
}
/**
* XEP-0060 Publish Subscribe (https://xmpp.org/extensions/xep-0060.html)
* XEP-0223 Persistent Storage of Private Data via PubSub (https://xmpp.org/extensions/xep-0223.html)
*/
class PublishSubscribePlugin extends AbstractXmppPlugin {
constructor(xmppChatAdapter, serviceDiscoveryPlugin) {
super();
this.xmppChatAdapter = xmppChatAdapter;
this.serviceDiscoveryPlugin = serviceDiscoveryPlugin;
this.publish$ = new Subject();
this.supportsPrivatePublish = new BehaviorSubject('unknown');
}
onBeforeOnline() {
return this.determineSupportForPrivatePublish();
}
onOffline() {
this.supportsPrivatePublish.next('unknown');
}
storePrivatePayloadPersistent(node, id, data) {
return new Promise((resolve, reject) => {
this.supportsPrivatePublish
.pipe(filter(support => support !== 'unknown'))
.subscribe((support) => {
if (!support) {
reject(new Error('does not support private publish subscribe'));
}
else {
resolve(this.xmppChatAdapter.chatConnectionService.sendIq(new PublishStanzaBuilder({ node, id, data, persistItems: true }).toStanza()));
}
});
});
}
privateNotify(node, data, id) {
return new Promise((resolve, reject) => {
this.supportsPrivatePublish
.pipe(filter(support => support !== 'unknown'))
.subscribe((support) => {
if (!support) {
reject(new Error('does not support private publish subscribe'));
}
else {
resolve(this.xmppChatAdapter.chatConnectionService.sendIq(new PublishStanzaBuilder({ node, id, data, persistItems: false }).toStanza()));
}
});
});
}
handleStanza(stanza) {
const eventElement = stanza.getChild('event', PUBSUB_EVENT_XMLNS);
if (stanza.is('message') && eventElement) {
this.publish$.next(eventElement);
return true;
}
return false;
}
retrieveNodeItems(node) {
return __awaiter(this, void 0, void 0, function* () {
try {
const iqResponseStanza = yield this.xmppChatAdapter.chatConnectionService.sendIq(new RetrieveDataStanzaBuilder(node).toStanza());
return iqResponseStanza.getChild('pubsub').getChild('items').getChildren('item');
}
catch (e) {
if (e instanceof IqResponseError &&
(e.errorCondition === 'item-not-found' || e.errorCode === 404)) {
return [];
}
throw e;
}
});
}
determineSupportForPrivatePublish() {
return __awaiter(this, void 0, void 0, function* () {
let isSupported;
try {
const service = yield this.serviceDiscoveryPlugin.findService('pubsub', 'pep');
isSupported = service.features.includes('http://jabber.org/protocol/pubsub#publish-options');
}
catch (e) {
isSupported = false;
}
this.supportsPrivatePublish.next(isSupported);
});
}
}
const MUC_SUB_FEATURE_ID = 'urn:xmpp:mucsub:0';
var MUC_SUB_EVENT_TYPE;
(function (MUC_SUB_EVENT_TYPE) {
MUC_SUB_EVENT_TYPE["presence"] = "urn:xmpp:mucsub:nodes:presence";
MUC_SUB_EVENT_TYPE["messages"] = "urn:xmpp:mucsub:nodes:messages";
MUC_SUB_EVENT_TYPE["affiliations"] = "urn:xmpp:mucsub:nodes:affiliations";
MUC_SUB_EVENT_TYPE["subscribers"] = "urn:xmpp:mucsub:nodes:subscribers";
MUC_SUB_EVENT_TYPE["config"] = "urn:xmpp:mucsub:nodes:config";
MUC_SUB_EVENT_TYPE["subject"] = "urn:xmpp:mucsub:nodes:subject";
MUC_SUB_EVENT_TYPE["system"] = "urn:xmpp:mucsub:nodes:system";
})(MUC_SUB_EVENT_TYPE || (MUC_SUB_EVENT_TYPE = {}));
/**
* support for https://docs.ejabberd.im/developer/xmpp-clients-bots/extensions/muc-sub/
*/
class MucSubPlugin extends AbstractXmppPlugin {
constructor(xmppChatAdapter, serviceDiscoveryPlugin) {
super();
this.xmppChatAdapter = xmppChatAdapter;
this.serviceDiscoveryPlugin = serviceDiscoveryPlugin;
this.supportsMucSub$ = new BehaviorSubject('unknown');
}
onBeforeOnline() {
return this.determineSupportForMucSub();
}
determineSupportForMucSub() {
return __awaiter(this, void 0, void 0, function* () {
let isSupported;
try {
const service = yield this.serviceDiscoveryPlugin.findService('conference', 'text');
isSupported = service.features.includes(MUC_SUB_FEATURE_ID);
}
catch (e) {
isSupported = false;
}
this.supportsMucSub$.next(isSupported);
});
}
onOffline() {
this.supportsMucSub$.next('unknown');
}
subscribeRoom(roomJid, nodes = []) {
return __awaiter(this, void 0, void 0, function* () {
const nick = this.xmppChatAdapter.chatConnectionService.userJid.local;
yield this.xmppChatAdapter.chatConnectionService.sendIq(makeSubscribeRoomStanza(roomJid, nick, nodes));
});
}
unsubscribeRoom(roomJid) {
return __awaiter(this, void 0, void 0, function* () {
yield this.xmppChatAdapter.chatConnectionService.sendIq(makeUnsubscribeRoomStanza(roomJid));
});
}
/**
* A room moderator can unsubscribe others providing the their jid as attribute to the information query (iq)
* see: https://docs.ejabberd.im/developer/xmpp-clients-bots/extensions/muc-sub/#unsubscribing-from-a-muc-room
* @param roomJid for the room to be unsubscribed from
* @param jid user id to be unsubscribed
*/
unsubscribeJidFromRoom(roomJid, jid) {
this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'set', to: roomJid }, xml('unsubscribe', { xmlns: 'urn:xmpp:mucsub:0', jid })));
}
/**
* A user can query the MUC service to get their list of subscriptions.
* see: https://docs.ejabberd.im/developer/xmpp-clients-bots/extensions/muc-sub/#g dd ddetting-list-of-subscribed-rooms
*/
getSubscribedRooms() {
return __awaiter(this, void 0, void 0, function* () {
const { local, domain } = this.xmppChatAdapter.chatConnectionService.userJid;
const from = `${local}@${domain}`;
const subscriptions = yield this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'get', from, to: 'muc.' + domain }, xml('subscriptions', { xmlns: 'urn:xmpp:mucsub:0' })));
return subscriptions.getChildren('subscription').map(sub => sub.getAttr('jid'));
});
}
/**
* A subscriber or room moderator can get the list of subscribers by sending <subscriptions/> request directly to the room JID.
* see: https://docs.ejabberd.im/developer/xmpp-clients-bots/extensions/muc-sub/#getting-list-of-subscribers-of-a-room
* @param roomJid of the room the get a subscriber list from
*/
getSubscribers(roomJid) {
this.xmppChatAdapter.chatConnectionService.sendIq(xml('iq', { type: 'get', to: roomJid }, xml('subscriptions', { xmlns: 'urn:xmpp:mucsub:0' })));
}
retrieveSubscriptions() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
const service = yield this.serviceDiscoveryPlugin.findService('conference', 'text');
const result = yield this.xmppChatAdapter.chatConnectionService.sendIq(makeRetrieveSubscriptionsStanza(service.jid));
const subscriptions = (_b = (_a = result
.getChild('subscriptions', MUC_SUB_FEATURE_ID)) === null || _a === void 0 ? void 0 : _a.getChildren('subscription')) === null || _b === void 0 ? void 0 : _b.map(subscriptionElement => {
var _a, _b;
const subscribedEvents = (_b = (_a = subscriptionElement
.getChildren('event')) === null || _a === void 0 ? void 0 : _a.map(eventElement => eventElement.attrs.node)) !== null && _b !== void 0 ? _b : [];
return [subscriptionElement.attrs.jid, subscribedEvents];
});
return new Map(subscriptions);
});
}
}
function makeSubscribeRoomStanza(roomJid, nick, nodes) {
return xml('iq', { type: 'set', to: roomJid }, xml('subscribe', { xmlns: MUC_SUB_FEATURE_ID, nick }, nodes.map(node => xml('event', { node }))));
}
function makeUnsubscribeRoomStanza(roomJid) {
return xml('iq', { type: 'set', to: roomJid }, xml('unsubscribe', { xmlns: MUC_SUB_FEATURE_ID }));
}
function makeRetrieveSubscriptionsStanza(conferenceServiceJid) {
return xml('iq', { type: 'get', to: conferenceServiceJid }, xml('subscriptions', { xmlns: MUC_SUB_FEATURE_ID }));
}
/**
* https://xmpp.org/extensions/xep-0313.html
* Message Archive Management
*/
class MessageArchivePlugin extends AbstractXmppPlugin {
constructor(chatService, serviceDiscoveryPlugin, multiUserChatPlugin, logService, messagePlugin) {
super();
this.chatService = chatService;
this.serviceDiscoveryPlugin = serviceDiscoveryPlugin;
this.multiUserChatPlugin = multiUserChatPlugin;
this.logService = logService;
this.messagePlugin = messagePlugin;
this.mamMessageReceived$ = new Subject();
this.chatService.state$
.pipe(filter(state => state === 'online'))
.subscribe(() => __awaiter(this, void 0, void 0, function* () {
if (yield this.supportsMessageArchiveManagement()) {
yield this.requestNewestMessages();
}
}));
// emit contacts to refresh contact list after receiving mam messages
this.mamMessageReceived$
.pipe(debounceTime(10))
.subscribe(() => this.chatService.contacts$.next(this.chatService.contacts$.getValue()));
}
requestNewestMessages() {
return __awaiter(this, void 0, void 0, function* () {
yield this.chatService.chatConnectionService.sendIq(xml('iq', { type: 'set' }, xml('query', { xmlns: 'urn:xmpp:mam:2' }, xml('set', { xmlns: 'http://jabber.org/protocol/rsm' }, xml('max', {}, 250), xml('before')))));
});
}
loadMostRecentUnloadedMessages(recipient) {
return __awaiter(this, void 0, void 0, function* () {
// for user-to-user chats no to-attribute is necessary, in case of multi-user-chats it has to be set to the bare room jid
const to = recipient.recipientType === 'room' ? recipient.roomJid.toString() : undefined;
const request = xml('iq', { type: 'set', to }, xml('query', { xmlns: 'urn:xmpp:mam:2' }, xml('x', { xmlns: 'jabber:x:data', type: 'submit' }, xml('field', { var: 'FORM_TYPE', type: 'hidden' }, xml('value', {}, 'urn:xmpp:mam:2')), recipient.recipientType === 'contact' ?
xml('field', { var: 'with' }, xml('value', {}, recipient.jidBare))
: undefined, recipient.oldestMessage ?
xml('field', { var: 'end' }, xml('value', {}, recipient.oldestMessage.datetime.toISOString()))
: undefined), xml('set', { xmlns: 'http://jabber.org/protocol/rsm' }, xml('max', {}, 100), xml('before'))));
yield this.chatService.chatConnectionService.sendIq(request);
});
}
loadAllMessages() {
return __awaiter(this, void 0, void 0, function* () {
if (!(yield this.supportsMessageArchiveManagement())) {
throw new Error('message archive management not suppported');
}
let lastMamResponse = yield this.chatService.chatConnectionService.sendIq(xml('iq', { type: 'set' }, xml('query', { xmlns: 'urn:xmpp:mam:2' })));
while (lastMamResponse.getChild('fin').attrs.complete !== 'true') {
const lastReceivedMessageId = lastMamResponse.getChild('fin').getChild('set').getChildText('last');
lastMamResponse = yield this.chatService.chatConnectionService.sendIq(xml('iq', { type: 'set' }, xml('query', { xmlns: 'urn:xmpp:mam:2' }, xml('set', { xmlns: 'http://jabber.org/protocol/rsm' }, xml('max', {}, 250), xml('after', {}, lastReceivedMessageId)))));
}
});
}
supportsMessageArchiveManagement() {
return __awaiter(this, void 0, void 0, function* () {
const supportsMessageArchiveManagement = yield this.serviceDiscoveryPlugin.supportsFeature(this.chatService.chatConnectionService.userJid.bare().toString(), 'urn:xmpp:mam:2');
if (!supportsMessageArchiveManagement) {
this.logService.info('server doesnt support MAM');
}
return supportsMessageArchiveManagement;
});
}
handleStanza(stanza) {
if (this.isMamMessageStanza(stanza)) {
this.handleMamMessageStanza(stanza);
return true;
}
return false;
}
isMamMessageStanza(stanza) {
const result = stanza.getChild('result');
return stanza.name === 'message' && (result === null || result === void 0 ? void 0 : result.attrs.xmlns) === 'urn:xmpp:mam:2';
}
handleMamMessageStanza(stanza) {
const forwardedElement = stanza.getChild('result').getChild('forwarded');
const messageElement = forwardedElement.getChild('message');
const delayElement = forwardedElement.getChild('delay');
const eventElement = messageElement.getChild('event', PUBSUB_EVENT_XMLNS);
if (messageElement.getAttr('type') == null && eventElement != null) {
this.handlePubSubEvent(eventElement, delayElement);
}
else {
this.handleArchivedMessage(messageElement, delayElement);
}
}
handleArchivedMessage(messageElement, delayEl) {
const type = messageElement.getAttr('type');
if (type === 'chat') {
const messageHandled = this.messagePlugin.handleStanza(messageElement, delayEl);
if (messageHandled) {
this.mamMessageReceived$.next();
}
}
else if (type === 'groupchat') {
this.multiUserChatPlugin.handleStanza(messageElement, delayEl);
}
else {
throw new Error(`unknown archived message type: ${type}`);
}
}
handlePubSubEvent(eventElement, delayElement) {
const itemsElement = eventElement.getChild('items');
const itemsNode = itemsElement === null || itemsElement === void 0 ? void 0 : itemsElement.attrs.node;
if (itemsNode !== MUC_SUB_EVENT_TYPE.messages) {
this.logService.warn(`Handling of MUC/Sub message types other than ${MUC_SUB_EVENT_TYPE.messages} isn't implemented yet!`);
return;
}
const itemElements = itemsElement.getChildren('item');
itemElements.forEach((itemEl) => this.handleArchivedMessage(itemEl.getChild('message'), delayElement));
}
}
class ChatWindowState {
constructor(recipient, isCollapsed) {
this.recipient = recipient;
this.isCollapsed = isCollapsed;
}
}
/**
* Used to open chat windows programmatically.
*/
class ChatListStateService {
constructor(chatService) {
this.chatService = chatService;
this.openChats$ = new BehaviorSubject([]);
this.openTracks$ = new BehaviorSubject([]);
this.chatService.state$
.pipe(filter(newState => newState === 'disconnected'))
.subscribe(() => {
this.openChats$.next([]);
});
this.chatService.contactRequestsReceived$.subscribe(contacts => {
for (const contact of contacts) {
this.openChat(contact);
}
});
}
openChatCollapsed(recipient) {
if (!this.isChatWithRecipientOpen(recipient)) {
const openChats = this.openChats$.getValue();
const chatWindow = new ChatWindowState(recipient, true);
const copyWithNewContact = [chatWindow].concat(openChats);
this.openChats$.next(copyWithNewContact);
}
}
openChat(recipient) {
this.openChatCollapsed(recipient);
this.findChatWindowStateByRecipient(recipient).isCollapsed = false;
}
closeChat(recipient) {
const openChats = this.openChats$.getValue();
const index = this.findChatWindowStateIndexByRecipient(recipient);
if (index >= 0) {
const copyWithoutContact = openChats.slice();
copyWithoutContact.splice(index, 1);
this.openChats$.next(copyWithoutContact);
}
}
openTrack(track) {
this.openTracks$.next(this.openTracks$.getValue().concat([track]));
}
closeTrack(track) {
this.openTracks$.next(this.openTracks$.getValue().filter(s => s !== track));
}
isChatWithRecipientOpen(recipient) {
return this.findChatWindowStateByRecipient(recipient) !== undefined;
}
findChatWindowStateIndexByRecipient(recipient) {
return this.openChats$.getValue()
.findIndex((chatWindowState) => chatWindowState.recipient.equalsBareJid(recipient));
}
findChatWindowStateByRecipient(recipient) {
return this.openChats$.getValue().find(chat => chat.recipient.equalsBareJid(recipient));
}
}
ChatListStateService.decorators = [
{ type: Injectable }
];
ChatListStateService.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [CHAT_SERVICE_TOKEN,] }] }
];
/**
* Used to determine if a message component for a given recipient is open.
*/
class ChatMessageListRegistryService {
constructor() {
this.openChats$ = new BehaviorSubject(new Set());
this.chatOpened$ = new Subject();
this.recipientToOpenMessageListCount = new Map();
}
isChatOpen(recipient) {
return this.getOrDefault(recipient, 0) > 0;
}
incrementOpenWindowCount(recipient) {
const wasWindowOpen = this.isChatOpen(recipient);
this.recipientToOpenMessageListCount.set(recipient, this.getOrDefault(recipient, 0) + 1);
const openWindowSet = this.openChats$.getValue();
openWindowSet.add(recipient);
this.openChats$.next(openWindowSet);
if (!wasWindowOpen) {
this.chatOpened$.next(recipient);
}
}
decrementOpenWindowCount(recipient) {
const newValue = this.getOrDefault(recipient, 0) - 1;
if (newValue <= 0) {
this.recipientToOpenMessageListCount.set(recipient, 0);
const openWindowSet = this.openChats$.getValue();
openWindowSet.delete(recipient);
this.openChats$.next(openWindowSet);
}
else {
this.recipientToOpenMessageListCount.set(recipient, newValue);
}
}
getOrDefault(recipient, defaultValue) {
return this.recipientToOpenMessageListCount.get(recipient) || defaultValue;
}
}
ChatMessageListRegistryService.decorators = [
{ type: Injectable }
];
ChatMessageListRegistryService.ctorParameters = () => [];
// tslint:disable
const dummyAvatarContact = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iNjAwIiBoZWlnaHQ9IjYwMCIgdmlld0JveD0iMCAwIDYwMCA2MDAiPgogIDxkZWZzPgogICAgPGNsaXBQYXRoIGlkPSJjbGlwLV8xIj4KICAgICAgPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI2MDAiLz4KICAgIDwvY2xpcFBhdGg+CiAgPC9kZWZzPgogIDxnIGlkPSJfMSIgZGF0YS1uYW1lPSIxIiBjbGlwLXBhdGg9InVybCgjY2xpcC1fMSkiPgogICAgPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI2MDAiIGZpbGw9IiNmZmYiLz4KICAgIDxnIGlkPSJHcnVwcGVfNzcxNyIgZGF0YS1uYW1lPSJHcnVwcGUgNzcxNyI+CiAgICAgIDxyZWN0IGlkPSJSZWNodGVja18xMzk3IiBkYXRhLW5hbWU9IlJlY2h0ZWNrIDEzOTciIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIiBmaWxsPSIjZTVlNmU4Ii8+CiAgICAgIDxlbGxpcHNlIGlkPSJFbGxpcHNlXzI4MyIgZGF0YS1uYW1lPSJFbGxpcHNlIDI4MyIgY3g9IjExNi4yMzEiIGN5PSIxMjUuNjcxIiByeD0iMTE2LjIzMSIgcnk9IjEyNS42NzEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE4NS4yMzEgMTExLjQ4NSkiIGZpbGw9IiNhZmI0YjgiLz4KICAgICAgPHBhdGggaWQ9IlBmYWRfMjQ5NjIiIGRhdGEtbmFtZT0iUGZhZCAyNDk2MiIgZD0iTTU0Ni4zNTksNTk1LjI3NnMwLTIxNy41NjMtMjQ0LjkwOS0yMTcuNTYzaC0xLjQ1N2MtMjQ0LjkwOSwwLTI0NC45MDksMjE3LjU2My0yNDQuOTA5LDIxNy41NjMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgNC43MjQpIiBmaWxsPSIjYWZiNGI4Ii8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K';
const dummyAvatarRoom = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iNjAwIiBoZWlnaHQ9IjYwMCIgdmlld0JveD0iMCAwIDYwMCA2MDAiPgogIDxkZWZzPgogICAgPGNsaXBQYXRoIGlkPSJjbGlwLV8zIj4KICAgICAgPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI2MDAiLz4KICAgIDwvY2xpcFBhdGg+CiAgPC9kZWZzPgogIDxnIGlkPSJfMyIgZGF0YS1uYW1lPSIzIiBjbGlwLXBhdGg9InVybCgjY2xpcC1fMykiPgogICAgPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI2MDAiIGZpbGw9IiNmZmYiLz4KICAgIDxnIGlkPSJHcnVwcGVfNzcxOCIgZGF0YS1uYW1lPSJHcnVwcGUgNzcxOCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTc4MC42OTcgODgxLjUpIj4KICAgICAgPHJlY3QgaWQ9IlJlY2h0ZWNrXzEzOTgiIGRhdGEtbmFtZT0iUmVjaHRlY2sgMTM5OCIgd2lkdGg9IjYwMCIgaGVpZ2h0PSI1OTkuOTk1IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3ODAuNjk3IC04ODEuNSkiIGZpbGw9IiNlNWU2ZTgiLz4KICAgICAgPGVsbGlwc2UgaWQ9IkVsbGlwc2VfMjg0IiBkYXRhLW5hbWU9IkVsbGlwc2UgMjg0IiBjeD0iMTE2LjIzMSIgY3k9IjEyNS42NzEiIHJ4PSIxMTYuMjMxIiByeT0iMTI1LjY3MSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTY1LjkyNiAtNzY5LjA5MykiIGZpbGw9IiNhZmI0YjgiLz4KICAgICAgPGVsbGlwc2UgaWQ9IkVsbGlwc2VfMjg1IiBkYXRhLW5hbWU9IkVsbGlwc2UgMjg1IiBjeD0iNjcuOTk4IiBjeT0iNzMuNTIxIiByeD0iNjcuOTk4IiByeT0iNzMuNTIxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4MTYuMjEgLTY2NS40OTYpIiBmaWxsPSIjYWZiNGI4Ii8+CiAgICAgIDxlbGxpcHNlIGlkPSJFbGxpcHNlXzI4OSIgZGF0YS1uYW1lPSJFbGxpcHNlIDI4OSIgY3g9IjY3Ljk5OCIgY3k9IjczLjUyMSIgcng9IjY3Ljk5OCIgcnk9IjczLjUyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIxMi4xMDcgLTY2NS40OTYpIiBmaWxsPSIjYWZiNGI4Ii8+CiAgICAgIDxwYXRoIGlkPSJQZmFkXzI0OTYzIiBkYXRhLW5hbWU9IlBmYWQgMjQ5NjMiIGQ9Ik0xMzI3LjA1Mi0yODYuMjI1czAtMjE3LjU2My0yNDQuOTA3LTIxNy41NjNoLTEuNDU3Yy0yNDQuOTA3LDAtMjQ0LjkwNywyMTcuNTYzLTI0NC45MDcsMjE3LjU2M1oiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgNC43MjUpIiBmaWxsPSIjYWZiNGI4Ii8+CiAgICAgIDxwYXRoIGlkPSJQZmFkXzI0OTY0IiBkYXRhLW5hbWU9IlBmYWQgMjQ5NjQiIGQ9Ik05MzMuOTc3LTQ4My44Yy0xLjA1LjYtMi4xLDEuMjItMy4xNCwxLjg0LTMyLjM0LDE5LjM0LTU4LjI5LDQ2LjI3LTc3LjEyLDgwLjA1LTMxLjcsNTYuODgtMzQuMzU1LDExOC43MjgtMzQuMzU1LDEyMS4yNDhoLTQwLjkxTDc4MC43LTQ3MS4zMmMyMy4yOC0xOC44Miw1Ny4wNS0zMi40NywxMDYuMDQtMzIuNDdoLjk0YTIxNy43NTMsMjE3Ljc1MywwLDAsMSw0My44Myw0LjE4QTguNTQ5LDguNTQ5LDAsMCwxLDkzMy45NzctNDgzLjhaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTAgLTAuNzI1KSIgZmlsbD0iI2FmYjRiOCIvPgogICAgICA8cGF0aCBpZD0iUGZhZF8yNDk2OCIgZGF0YS1uYW1lPSJQZmFkIDI0OTY4IiBkPSJNNzgyLjc5LTQ4My44YzEuMDUuNiwyLjEsMS4yMiwzLjE0LDEuODQsMzIuMzQsMTkuMzQsNTguMjksNDYuMjcsNzcuMTIsODAuMDUsMzEuNyw1Ni44OCwzNC4zNTUsMTE4LjcyOCwzNC4zNTUsMTIxLjI0OGg0MC45MUw5MzYuMDctNDcxLjMyYy0yMy4yOC0xOC44Mi01Ny4wNS0zMi40Ny0xMDYuMDQtMzIuNDdoLS45NGEyMTcuNzUzLDIxNy43NTMsMCwwLDAtNDMuODMsNC4xOEE4LjU0OSw4LjU0OSwwLDAsMCw3ODIuNzktNDgzLjhaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0NTcuNTQ3IC0wLjcyNikiIGZpbGw9IiNhZmI0YjgiLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=';
const chatAdminUserName = 'admin@chat.mahamma.com';
const chatAdminPassword = 'tDm2R&nMRr47w!dL';
const getAvatarUrl = 'https://chat.mahamma.com:5443/api/get_vcard';
const identity = elem => elem;
const toString = elem => elem.toString();
/**
* given a sorted list, insert the given item in place after the last matching item.
* @param elemToInsert the item to insert
* @param list the list in which the element should be inserted
* @param keyExtractor an optional element mapper, defaults to toString
*/
function insertSortedLast(elemToInsert, list, keyExtractor = toString) {
list.splice(findSortedInsertionIndexLast(keyExtractor(elemToInsert), list, keyExtractor), 0, elemToInsert);
}
/**
* Find the highest possible index where the given element should be inserted so that the order of the list is preserved.
* @param needle the needle to find
* @param haystack the pre sorted list
* @param keyExtractor an optional needle mapper, defaults to toString
*/
function findSortedInsertionIndexLast(needle, haystack, keyExtractor = toString) {
let low = 0;
let high = haystack.length;
while (low !== high) {
const cur = Math.floor(low + (high - low) / 2);
if (needle < keyExtractor(haystack[cur])) {
high = cur;
}
else {
low = cur + 1;
}
}
return low;
}
/**
* Find the index of an element in a sorted list. If list contains no matching element, return -1.
*/
function findSortedIndex(needle, haystack, keyExtractor = toString) {
let low = 0;
let high = haystack.length;
while (low !== high) {
const cur = Math.floor(low + (high - low) / 2);
const extractedKey = keyExtractor(haystack[cur]);
if (needle < extractedKey) {
high = cur;
}
else if (needle > extractedKey) {
low = cur + 1;
}
else {
return cur;
}
}
return -1;
}
/**
* Like {@link Array.prototype.findIndex} but finds the last index instead.
*/
function findLastIndex(arr, predicate) {
for (let i = arr.length - 1; i >= 0; i--) {
if (predicate(arr[i])) {
return i;
}
}
return -1;
}
/**
* Like {@link Array.prototype.find} but finds the last matching element instead.
*/
function findLast(arr, predicate) {
return arr[findLastIndex(arr, predicate)];
}
/**
* Return a new array, where all elements from the original array occur exactly once.
*/
function removeDuplicates(arr, eq = (x, y) => x === y) {
const results = [];
for (const arrElement of arr) {
let duplicateFound = false;
for (const resultElement of results) {
if (eq(arrElement, resultElement)) {
duplicateFound = true;
break;
}
}
if (!duplicateFound) {
results.push(arrElement);
}
}
return results;
}
/**
* converts date objects to date strings like '2011-10-05'
*/
function extractDateStringFromDate(date) {
const isoString = date.toISOString();
return isoString.slice(0, isoString.indexOf('T'));
}
class MessageStore {
constructor(logService) {
this.logService = logService;
this.messages = [];
this.dateMessageGroups = [];
this.messageIdToMessage = new Map();
this.messages$ = new Subject();
}
addMessage(message) {
if (message.id && this.messageIdToMessage.has(message.id)) {
if (this.logService) {
this.logService.warn(`message with id ${message.id} already exists`);
}
return false;
}
insertSortedLast(message, this.messages, m => m.datetime);
this.addToDateMessageGroups(message);
this.messageIdToMessage.set(message.id, message);
this.messages$.next(message);
return true;
}
get oldestMessage() {
return this.messages[0];
}
get mostRecentMessage() {
return this.messages[this.messages.length - 1];
}
get mostRecentMessageReceived() {
return findLast(this.messages, msg => msg.direction === Direction.in);
}
get mostRecentMessageSent() {
return findLast(this.messages, msg => msg.direction === Direction.out);
}
addToDateMessageGroups(message) {
const dateString = extractDateStringFromDate(message.datetime);
const groupIndex = findSortedIndex(dateString, this.dateMessageGroups, group => extractDateStringFromDate(group.date));
if (groupIndex !== -1) {
insertSortedLast(message, this.dateMessageGroups[groupIndex].messages, m => m.datetime);
}
else {
const groupToInsert = {
date: message.datetime,
messages: [message]
};
const insertIndex = findSortedInsertionIndexLast(dateString, this.dateMessageGroups, group => extractDateStringFromDate(group.date));
this.dateMessageGroups.splice(insertIndex, 0, groupToInsert);
}
}
}
var Presence;
(function (Presence) {
Presence["present"] = "present";
Presence["unavailable"] = "unavailable";
Presence["away"] = "away";
})(Presence || (Presence = {}));
function isJid(o) {
// due to unknown reasons, `o instanceof JID` does not work when
// JID is instantiated by an application instead of ngx-chat
return !!o.bare;
}
var ContactSubscription;
(function (ContactSubscription) {
ContactSubscription["to"] = "to";
ContactSubscription["from"] = "from";
ContactSubscription["both"] = "both";
ContactSubscription["none"] = "none";
})(ContactSubscription || (ContactSubscription = {}));
class Contact {
/**
* Do not call directly, use {@link ContactFactoryService#createContact} instead.
*/
constructor(httpClinet, jidPlain, name, nick, logService, avatar) {
this.httpClinet = httpClinet;
this.name = name;
this.nick = nick;
this.recipientType = "contact";
this.avatar = dummyAvatarContact;
this.chatUserName = chatAdminUserName;
this.chatPassword = chatAdminPassword;
this.avatarUrl = getAvatarUrl;
this.metadata = {};
// private _httpHandler: HttpHandler;
this.presence$ = new BehaviorSubject(Presence.unavailable);
this.subscription$ = new BehaviorSubject(ContactSubscription.none);
this.pendingOut$ = new BehaviorSubject(false);
this.pendingIn$ = new BehaviorSubject(false);
this.resources$ = new BehaviorSubject(new Map());
this._httpClient = httpClinet;
const jid$1 = jid(jidPlain);
this.jidFull = jid$1;
this.jidBare = jid$1.bare();
let user = {
user: jid$1.local,
host: jid$1.domain,
name: "URL",
};
let credentials = this.chatUserName + ":" + this.chatPassword;
debugger;
const httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
Authorization:
//"Basic " + btoa("admin@chat.mahamma.com:tDm2R&nMRr47w!dL"),
"Basic " + btoa(credentials),
}),
};
this._httpClient.post(this.avatarUrl, user, httpOptions)
.subscribe((result) => {
debugger;
this.avatar = result.content;
});
// this.avatar ="https://picsum.photos/200/300";
this.messageStore = new MessageStore(logService);
}
get messages$() {
return this.messageStore.messages$;
}
get messages() {
return this.messageStore.messages;
}
get dateMessagesGroups() {
return this.messageStore.dateMessageGroups;
}
get oldestMessage() {
return this.messageStore.oldestMessage;
}
get mostRecentMessage() {
return this.messageStore.mostRecentMessage;
}
get mostRecentMessageReceived() {
return this.messageStore.mostRecentMessageReceived;
}
get mostRecentMessageSent() {
return this.messageStore.mostRecentMessageSent;
}
addMessage(message) {
this.messageStore.addMessage(message);
}
equalsBareJid(other) {
if (other instanceof Contact || isJid(other)) {
const otherJid = other instanceof Contact ? other.jidBare : other.bare();
return this.jidBare.equals(otherJid);
}
return false;
}
isSubscribed() {
const subscription = this.subscription$.getValue();
return (subscription === ContactSubscription.both ||
subscription === ContactSubscription.to);
}
isUnaffiliated() {
return (!this.isSubscribed() &&
!this.pendingIn$.getValue() &&
!this.pendingOut$.getValue());
}
updateResourcePresence(jid, presence) {
const resources = this.resources$.getValue();
resources.set(jid, presence);
this.presence$.next(this.determineOverallPresence(resources));
this.resources$.next(resources);
}
getMessageById(id) {
return this.messageStore.messageIdToMessage.get(id);
}
determineOverallPresence(jidToPresence) {
let result = Presence.unavailable;
[...jidToPresence.values()].some((presence) => {
if (presence === Presence.present) {
result = presence;
return true;
}
else if (presence === Presence.away) {
result = Presence.away;
}
return false;
});
return result;
}
}
var LogLevel;
(function (LogLevel) {
LogLevel[LogLevel["Disabled"] = 0] = "Disabled";
LogLevel[LogLevel["Error"] = 1] = "Error";
LogLevel[LogLevel["Warn"] = 2] = "Warn";
LogLevel[LogLevel["Info"] = 3] = "Info";
LogLevel[LogLevel["Debug"] = 4] = "Debug";
})(LogLevel || (LogLevel = {}));
class LogService {
constructor() {
this.logLevel = LogLevel.Info;
this.writer = console;
this.messagePrefix = () => 'ChatService:';
}
error(...messages) {
if (this.logLevel >= LogLevel.Error) {
this.writer.error(this.messagePrefix(), ...messages);
}
}
warn(...messages) {
if (this.logLevel >= LogLevel.Warn) {
this.writer.warn(this.messagePrefix(), ...messages);
}
}
info(...messages) {
if (this.logLevel >= LogLevel.Info) {
this.writer.info(this.messagePrefix(), ...messages);
}
}
debug(...messages) {
if (this.logLevel >= LogLevel.Debug) {
this.writer.debug(this.messagePrefix(), ...messages);
}
}
}
LogService.decorators = [
{ type: Injectable }
];
class ContactFactoryService {
constructor(logService, httpClien