Home Reference Source

src/contacts/PresenceManager.js

import {EventEmitter2} from 'eventemitter2';
import {bindMethods} from '../utils';
import {PresenceQueue} from './PresenceQueue';
import {PresenceCollection} from './PresenceCollection';

/**
 * Element that manages presences.
 * It stores subscribed presences in a local collection that is
 * updated when WacProxy notifies a change.
 * @private
 */
export class PresenceManager {

	static of(presenceService) {
		return new PresenceManager(presenceService);
	}

	/**
	 * Constructs a Presence Manager
	 * @param {PresenceService} presenceService
	 * @private
	 */
	constructor(presenceService) {
		this.presenceService = presenceService;

		this.emitter = new EventEmitter2();
		this.emitter.setMaxListeners(0);

		/**
		 * Property used to determine if PresenceManager has been intialized
		 * and wait until initialization success.
		 * @private
		 * @type {Promise}
		 */
		this._init = null;

		/**
		 * Collection of subscribed presences.
		 * @private
		 * @type {PresenceCollection}
		 */
		this._collection = null;

		/**
		 * Queue that subscribes to presences through WacProxy.
		 * @private
		 * @type {PresenceQueue}
		 */
		this._queue = null;

		bindMethods(this, [
			'onPresenceUpdated',
		]);

		// init() is now sync, so... gas!
		this.init();
	}

	/**
	 * Initialize PresenceManager.
	 * It retrieves already subscribed presences from WacStack and subscribe
	 * to presence updates.
	 * @return {Promise} resolved after initialization.
	 */
	init() {
		this._collection = new PresenceCollection();
		this._queue = new PresenceQueue(this.presenceService, this._collection);
		this.presenceService.emitter.on('presence-updated', this.onPresenceUpdated);
		this._init = true;
	}

	/**
	 * Deinitialize PresenceManager.
	 */
	uninit() {
		this.presenceService.emitter.off('presence-update', this.onPresenceUpdated);
		this.presenceService = null;
		this._init = null;
		this._collection = null;
		this._queue = null;
	}

	/**
	 * Check if PresenceManager has been initialized and wait until
	 * initialization success.
	 * @return {Promise} resolved after initialization.
	 */
	isInitialized() {
		if (!this._init) {
			this.init();
		}
	}

	/**
	 * Request the presence associated with given address and subscribe
	 * to presence updates.
	 * If such address is present into local collection, this method will return it.
	 * Otherwise a new subscription will be created through subscription queue.
	 * @param {String} address The address associated with requested presence.
	 * @return {Promise<object>} resolved with presence object.
	 */
	async subscribeToPresence(address) {
		return await this._collection.getPresence(address) || this._queue.subscribeToPresence(address);
	}

	/**
	 * Unsubscribe from presence updates associated with given address.
	 * @param {String} address The presence address to stop receiving updates.
	 * @return {Promise} resolved when unsubscribed from presence updates.
	 */
	async unsubscribeFromPresence(address) {
		this._collection.removePresence(address);
		return this._queue.unsubscribeFromPresence(address);
	}

	/**
	 * Callback called when WacProxy notifies a presence update.
	 * @private
	 * @param {object} presence The presence that has been updated.
	 */
	onPresenceUpdated(presence) {
		if (this._collection) {
			this._collection.updatePresence(presence);
		}
		this.emitter.emit('updated', presence);
	}

	updatePresence(address, presence) {
		const updatedPresence = Object.assign({}, presence, {address});
		if (this._collection) {
			this._collection.updatePresence(updatedPresence);
		}
		return this.presenceService.update(address, presence);
	}

	getPresence(address) {
		return this.subscribeToPresence(address);
	}
}