UNPKG

@jsxc/jsxc

Version:

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

397 lines (291 loc) 11.4 kB
import Storage from './Storage'; import { IConnection } from './connection/Connection.interface'; import Connector from './connection/xmpp/Connector'; import StorageConnection from './connection/storage/Connection'; import JID from './JID'; import Contact from './Contact'; import { Presence } from './connection/AbstractConnection'; import Client from './Client'; import { NoticeManager } from './NoticeManager'; import PluginRepository from './plugin/PluginRepository'; import DiscoInfoRepository from './DiscoInfoRepository'; import DiscoInfoChangeable from './DiscoInfoChangeable'; import HookRepository from './util/HookRepository'; import Options from './Options'; import UUID from './util/UUID'; import Pipe from './util/Pipe'; import ChatWindow from '@ui/ChatWindow'; import { IJID } from './JID.interface'; import { IContact } from './Contact.interface'; import RosterContactProvider from './RosterContactProvider'; import ContactManager from './ContactManager'; import FallbackContactProvider from './FallbackContactProvider'; import Log from '@util/Log'; import CommandRepository from './CommandRepository'; import AvatarSet from '@ui/AvatarSet'; import { IAvatar } from './Avatar.interface'; import CallManager from './CallManager'; import MenuChatMessage from './MenuChatMessage'; type ConnectionCallback = (status: number, condition?: string) => void; export default class Account { private storage: Storage; private sessionStorage: Storage; private sessionId: string; private uid: string; private connection: IConnection; private connector: Connector; private contact: Contact; private noticeManager: NoticeManager; private pluginRepository: PluginRepository; private discoInfoRepository: DiscoInfoRepository; private ownDiscoInfo: DiscoInfoChangeable; private hookRepository = new HookRepository(); private contactManager: ContactManager; private commandRepository: CommandRepository; private callManager: CallManager; private chatMessageMenu: MenuChatMessage; private options: Options; private pipes = {}; constructor(url: string, jid: string, sid: string, rid: string); constructor(url: string, jid: string, password: string); constructor(uid: string); constructor() { if (arguments.length === 1) { this.uid = arguments[0]; this.sessionId = this.getStorage().getItem('sessionId'); } else if (arguments.length === 3 || arguments.length === 4) { let jid = new JID(arguments[1]); // anonymous accounts start without node this.uid = jid.node ? jid.bare : UUID.v4().slice(0, 8) + '=' + jid.domain; this.sessionId = UUID.v4(); let oldSessionId = this.getStorage().getItem('sessionId'); this.getStorage().setItem('sessionId', this.sessionId); if (oldSessionId) { Storage.clear(this.uid + '@' + oldSessionId); } } else { throw new Error('Unsupported number of arguments'); } this.options = new Options(this.getStorage()); this.connector = new Connector(this, arguments[0], arguments[1], arguments[2], arguments[3]); this.connection = new StorageConnection(this); if (arguments.length === 1) { this.pluginRepository = new PluginRepository(this); } this.contact = new Contact(this, new JID(this.uid), this.uid); let rosterContactProvider = new RosterContactProvider(this.getContactManager(), this); this.getContactManager().registerContactProvider(rosterContactProvider); let fallbackContactProvider = new FallbackContactProvider(this.getContactManager(), this); this.getContactManager().registerContactProvider(fallbackContactProvider); let connectionCallback = this.getOption('connectionCallback'); if (typeof connectionCallback === 'function') { this.registerConnectionHook((status, condition) => { connectionCallback(this.uid, status, condition); }); } this.getContactManager().restoreCache(); this.getNoticeManager(); } public getOptions(): Options { return this.options; } public getOption(key: string) { return this.options.get(key); } public setOption(key: string, value: any) { this.options.set(key, value); } public connect = (pause: boolean = false): Promise<void> => { let targetPresence = Client.getPresenceController().getTargetPresence(); if (targetPresence === Presence.offline) { Client.getPresenceController().setTargetPresence(Presence.online); } return this.connector .connect() .then(async ([status, connection]) => { this.connection = connection; if (pause) { connection.pause(); } let storage = this.getSessionStorage(); if (!storage.getItem('connection', 'created')) { storage.setItem('connection', 'created', new Date()); } if (!storage.getItem('options', 'loaded')) { let jid = this.connector.getJID(); await Options.load(jid.bare, this.connector.getPassword(), jid); this.connector.clearPassword(); storage.setItem('options', 'loaded', true); } if (!pause) { this.initConnection(status); } }) .catch(err => { if (Client.getAccountManager().getAccounts().length <= 1) { Client.getPresenceController().setTargetPresence(Presence.offline); } throw err; }); }; private async initConnection(status): Promise<void> { let storage = this.getSessionStorage(); if (storage.getItem('connection', 'inited')) { return; } await this.getContactManager().loadContacts(); let targetPresence = Client.getPresenceController().getTargetPresence(); this.getConnection().sendPresence(targetPresence); storage.setItem('connection', 'inited', true); } public triggerPresenceHook = (contact: IContact, presence, oldPresence) => { this.hookRepository.trigger('presence', contact, presence, oldPresence); }; public registerPresenceHook = func => { this.hookRepository.registerHook('presence', func); }; public triggerConnectionHook = (status: number, condition?: string) => { this.hookRepository.trigger('connection', status, condition); }; public registerConnectionHook = (func: ConnectionCallback) => { this.hookRepository.registerHook('connection', func); }; public triggerChatWindowInitializedHook = (chatWindow: ChatWindow, contact: Contact) => { this.hookRepository.trigger('chatWindowInitialized', chatWindow, contact); }; public registerChatWindowInitializedHook = (func: (chatWindow?: ChatWindow, contact?: Contact) => void) => { this.hookRepository.registerHook('chatWindowInitialized', func); }; public triggerChatWindowClearedHook = (chatWindow: ChatWindow, contact: Contact) => { this.hookRepository.trigger('chatWindowCleared', chatWindow, contact); }; public registerChatWindowClearedHook = (func: (chatWindow?: ChatWindow, contact?: Contact) => void) => { this.hookRepository.registerHook('chatWindowCleared', func); }; public getContactManager(): ContactManager { if (!this.contactManager) { this.contactManager = new ContactManager(this); } return this.contactManager; } public getPluginRepository(): PluginRepository { return this.pluginRepository; } public getDiscoInfoRepository(): DiscoInfoRepository { if (!this.discoInfoRepository) { this.discoInfoRepository = new DiscoInfoRepository(this); } return this.discoInfoRepository; } public getDiscoInfo(): DiscoInfoChangeable { if (!this.ownDiscoInfo) { this.ownDiscoInfo = new DiscoInfoChangeable(this.uid); } return this.ownDiscoInfo; } public getCommandRepository(): CommandRepository { if (!this.commandRepository) { this.commandRepository = new CommandRepository(); } return this.commandRepository; } public getCallManager() { if (!this.callManager) { this.callManager = new CallManager(this); } return this.callManager; } public getChatMessageMenu(): MenuChatMessage { if (!this.chatMessageMenu) { this.chatMessageMenu = new MenuChatMessage(); } return this.chatMessageMenu; } public getContact(jid?: IJID): IContact { return jid && jid.bare !== this.getJID().bare ? this.getContactManager().getContact(jid) : this.contact; } public getNoticeManager(): NoticeManager { if (!this.noticeManager) { this.noticeManager = new NoticeManager(this.getStorage()); } return this.noticeManager; } public getStorage() { if (!this.storage) { this.storage = new Storage(this.uid); } return this.storage; } public getSessionStorage() { if (!this.sessionStorage) { let name = this.uid + '@' + this.sessionId; this.sessionStorage = new Storage(name); } return this.sessionStorage; } public getPresence(): Presence { let sessionStorage = this.getSessionStorage(); let presence = sessionStorage.getItem('presence'); return typeof presence === 'number' ? presence : Presence.offline; } public setPresence(presence: Presence) { this.getSessionStorage().setItem('presence', presence); } public getConnection(): IConnection { return this.connection; } public getUid(): string { return this.uid; } public getSessionId(): string { return this.sessionId; } public getJID(): JID { let jid = this.connector.getJID(); if (!jid) { Log.warn('Empty JID for account', this.getUid()); } return jid; } public getConnectionUrl(): string { return this.connector.getUrl(); } public async updateAvatar(avatar: IAvatar | null) { const pipe = this.getPipe<[IAvatar]>('publishAvatar'); const [processedAvatar] = await pipe.run(avatar); if (processedAvatar && avatar !== null) { Log.warn('Avatar could not be updated'); return; } let avatarUI = AvatarSet.get(this.getContact()); avatarUI.reload(); this.getConnection().sendPresence(); } public getPipe<params extends any[] = any[]>(name: string): Pipe<params> { if (!this.pipes[name]) { this.pipes[name] = new Pipe<params>(); } return this.pipes[name]; } public remove() { this.destroy(); Client.getAccountManager().removeAccount(this); } public destroy() { this.getContactManager().removeAllContactsFromCache(); this.getConnection().close(); this.getStorage().destroy(); this.getSessionStorage().destroy(); this.getNoticeManager().removeAll(); for (const name in this.pipes) { this.pipes[name].destroy(); } if (this.getPluginRepository()) { this.getPluginRepository().destroyAllPlugins(); } } public connectionDisconnected() { this.setPresence(Presence.offline); this.remove(); } }