UNPKG

@jsxc/jsxc

Version:

Real-time XMPP chat application with video calls, file transfer and encrypted communication

262 lines (197 loc) 6.84 kB
import Message from './Message'; import { IMessage as IMessage, DIRECTION } from './Message.interface'; import Contact from './Contact'; import Storage from './Storage'; import PersistentMap from './util/PersistentMap'; import Log from '@util/Log'; import Client from './Client'; export default class Transcript { private properties: PersistentMap; private firstMessage: IMessage; private lastMessage: IMessage; private messages: { [index: string]: IMessage } = {}; constructor(storage: Storage, private contact: Contact) { this.properties = new PersistentMap(storage, 'transcript', contact.getId()); this.properties.registerHook('firstMessageId', firstMessageId => { this.firstMessage = this.getMessage(firstMessageId); }); } public unshiftMessage(message: IMessage) { let lastMessage = this.getLastMessage(); if (lastMessage) { lastMessage.setNext(message); } else { this.pushMessage(message); } message.setNext(undefined); this.lastMessage = message; } public pushMessage(message: IMessage) { if (!message.getNextId() && this.firstMessage) { message.setNext(this.firstMessage); } this.addMessage(message); if (message.getDirection() !== DIRECTION.SYS) { this.contact.setLastMessageDate(message.getStamp()); } this.properties.set('firstMessageId', message.getUid()); this.deleteLastMessages(); } public getFirstChatMessage(): IMessage { for (let message of this.getGenerator()) { if (!message.isSystem() && !message.isReplacement()) { return message; } } } public getFirstIncomingMessage(): IMessage { for (let message of this.getGenerator()) { if (message.isIncoming() && !message.isReplacement()) { return message; } } } public getFirstOutgoingMessage(): IMessage { for (let message of this.getGenerator()) { if (message.isOutgoing() && !message.isReplacement()) { return message; } } } public getFirstMessage(): IMessage { if (!this.firstMessage && this.properties.get('firstMessageId')) { this.firstMessage = this.getMessage(this.properties.get('firstMessageId')); } return this.firstMessage; } public getFirstOriginalMessage(): IMessage { for (let message of this.getGenerator()) { if (!message.isReplacement()) { return message; } } } public getLastMessage(): IMessage { if (this.lastMessage) { return this.lastMessage; } let ids = []; let lastMessage = this.getFirstMessage(); while (lastMessage && lastMessage.getNextId()) { let id = lastMessage.getNextId(); if (ids.indexOf(id) > -1) { Log.debug('Loop detected'); break; } ids.push(id); lastMessage = this.getMessage(id); } return (this.lastMessage = lastMessage); } public getMessage(id: string): IMessage { if (!this.messages[id] && id) { try { this.messages[id] = new Message(id); this.messages[id].registerHook('unread', unread => { if (!unread) { this.removeMessageFromUnreadMessages(this.messages[id]); } }); } catch (err) { Log.warn(err); return undefined; } } return this.messages[id]; } public *getGenerator() { let message = this.getFirstMessage(); while (message) { yield message; let nextId = message.getNextId(); message = nextId ? this.getMessage(nextId) : undefined; } } public findMessageByAttrId(attrId: string): IMessage { for (let message of this.getGenerator()) { if (message.getAttrId() === attrId) { return message; } } } private deleteLastMessages() { let allowedNumberOfMessages = parseInt(Client.getOption('numberOfMessages'), 10); let numberOfMessages = 0; if (allowedNumberOfMessages <= 0 || isNaN(allowedNumberOfMessages)) { return; } let message = this.getFirstMessage(); let nextMessage: IMessage; while (message) { nextMessage = this.getMessage(message.getNextId()); numberOfMessages++; if (numberOfMessages === allowedNumberOfMessages) { message.setNext(undefined); } else if (numberOfMessages > allowedNumberOfMessages) { message.delete(); } message = nextMessage; } } public clear() { let message = this.getFirstMessage(); let nextMessage: IMessage; while (message) { nextMessage = this.getMessage(message.getNextId()); message.delete(); message = nextMessage; } this.messages = {}; this.firstMessage = undefined; this.lastMessage = undefined; this.properties.remove('firstMessageId'); } public registerNewMessageHook(func: (newValue: any, oldValue: any) => void) { this.registerHook('firstMessageId', func); } public registerHook(property: string, func: (newValue: any, oldValue: any) => void) { this.properties.registerHook(property, func); } public markAllMessagesAsRead() { let unreadMessageIds = this.properties.get('unreadMessageIds') || []; for (let id of unreadMessageIds) { let message = this.messages[id]; if (message) { message.read(); } } this.properties.set('unreadMessageIds', []); } public getNumberOfUnreadMessages(): number { let unreadMessageIds = this.properties.get('unreadMessageIds') || []; return unreadMessageIds.length; } private removeMessageFromUnreadMessages(message: IMessage) { let unreadMessageIds: string[] = this.properties.get('unreadMessageIds') || []; if (message && unreadMessageIds.includes(message.getUid())) { this.properties.set( 'unreadMessageIds', unreadMessageIds.filter(id => id !== message.getUid()) ); } } private addMessage(message: IMessage) { let id = message.getUid(); this.messages[id] = message; if (message.getDirection() !== DIRECTION.OUT && message.isUnread()) { let unreadMessageIds = this.properties.get('unreadMessageIds') || []; unreadMessageIds.push(id); this.properties.set('unreadMessageIds', unreadMessageIds); message.registerHook('unread', unread => { if (!unread) { this.removeMessageFromUnreadMessages(message); } }); } } }