Home Reference Source

src/meetings/MeetingManager.js

import {EventEmitter} from '../eventemitter';
import {bindMethods} from '../utils';
import Meeting from './Meeting';

/**
 * ContactManager instance is obtained by calling the {@link Session#getMeetingManager}
 * method of {@link Session} and must not be directly instantiated.
 * Once the MeetingManager instance is obtained {@link MeetingManager#init} method
 * must be called to initialize it.
 *
 * ## Events
 *
 * - **`create`** is emitted every time a new meeting is created.
 *   - {@link Meeting} `meeting` The meeting that has just been created.
 * - **`delete`** is emitted when some meeting is deleted.
 *   - {@link Meeting} `meeting` The meeting that has just been deleted.
 *
 * @example <caption>Get meeting list</caption>
 * meetingManager.init().then(function() {
 *   meetingManager.getMeetings().forEach(function(meeting) {
 *     console.log(meeting.name);
 *   });
 * });
 */
export class MeetingManager extends EventEmitter {

	/** @protected */
	constructor(meetingService) {
		super();
		this._meetingService = meetingService;

		/**
		 * @type {Array<Meeting>}
		 */
		this._meetings = [];

		/**
		 * @type {Promise}
		 */
		this._initializing = null;

		/**
		 * The text to be sent by SMS to the participants in the meetings. The {{url}} string will be expanded with the
		 * meeting link
		 *
		 * @type {String}
		 */
		this._message = '';

		bindMethods(this, [
			'onUserInMeeting',
			'onLocalMeetingRemove',
		]);
	}


	/**
	 * Initializes the meeting manager
	 * @param {Object} query Object used for filtering the meetings
	 * @returns {Promise<ContactManager, Error>} A promise that returns when fulfilled
	 * the contact manager instance
	 */
	init(query = {}) {
		if (this._initializing !== null) {
			return this._initializing;
		}
		this._meetingService.emitter.on('update', this.onUserInMeeting);
		this._initializing = this._meetingService.get(query).then((meetings) => {
			meetings.forEach((meeting) => {
				if (this._meetings.some(elem => elem.id === meeting.id)) {
					return meeting;
				}
				let oMeeting = new Meeting(this._meetingService, meeting);
				this._meetings.push(oMeeting);
				this.emit('meeting-recovered', oMeeting);
			});
			return this;
		});
		return this._initializing;
	}

	/**
	 * Uninitializes the meeting manager
	 * @returns {Promise<undefined, Error>} A promise that is fulfilled when unitialization finishes
	 */
	uninit() {
		if (this._initializing === null) {
			return Promise.resolve();
		}
		return this._initializing.then(() => {
			this._meetingService.emitter.off('update', this.onUserInMeeting);
			this._meetings = [];
			this._initializing = null;
		});
	}


	/**
	 * Creates a meeting
	 * @param {string} name The name of the meeting.
	 * @param {Object[]} participants The participants of the new meeting session.
	 * @param {number} validSince The timestamp of the moment when the meeting starts.
	 * @param {boolean} [sendSmsViaWac=false] If true, an invitation for the meeting will be sent in an SMS by the server.
	 * @return {Promise<Meeting>} The just created meeting
	 */
	createMeeting(name, participants, validSince, sendSmsViaWac = false) {
		return Promise.resolve().then(() => {
			this._checkInitializingOfService();
			if (sendSmsViaWac) {
				participants = this._insertSendSMSFlagInParticipants(participants);
			}
			return this._meetingService.create({name, participants, validSince});
		}).then((meetingCreated) => {
			return {
				meeting: this._addMeeting(meetingCreated),
				phones: this._getPhonesFromParticipants(participants),
			};
		});
	}

	/**
	 * Update a meeting
	 * @param {String} id Meeting id
	 * @param {String} name Name of the meeting updated
	 * @param {Array} participants Array with the participants of the meeting updated
	 * @param {Object} validSince Updated date formatted string
	 * @param {boolean} sendSmsViaWac Flag used to decided
	 */
	updateMeeting(id, name, participants, validSince, sendSmsViaWac) {
		return Promise.resolve().then(() => {
			this._checkInitializingOfService();
			if (sendSmsViaWac) {
				participants = this._insertSendSMSFlagInParticipants(participants);
			}
			return this._meetingService.update(id, {name, participants, validSince});
		}).then((meetingUpdated) => {
			this._updateMeeting(meetingUpdated);
			return meetingUpdated;
		});
	}

	/**
	 * Insert a flag in each participant to indicate if the WAC
	 * has to send the invite or not
	 * @params {Array<Object>} participants
	 * @return {Array<Object} Updated participants
	 */
	_insertSendSMSFlagInParticipants(participants) {
		return participants.map((participant) => {
			participant.sendSMS = true;
			return participant;
		});
	}


	/**
	 * Retrieves a list with the available meetings
	 * @return {Array<Meetings>}
	 */
	getMeetings() {
		return this._meetings;
	}

	/**
	 * Set the message to send in a SMS
	 * @param  {String} value Text for the SMS
	 */
	set message(value) {
		this._message = value;
	}

	/**
	 * Adds a new contact to the list
	 * @private
	 * @param {Meeting} data
	 * @return {Meeting} The new created meeting
	 */
	_addMeeting(data) {
		if (this._meetings.some(meeting => meeting.id === data.id)) {
			return;
		}
		const meeting = new Meeting(this._meetingService, data);
		this._meetings.push(meeting);
		this.emit('create', meeting);
		return meeting;
	}

	/**
	 * Update an exisitng meeting in the meeting list
	 * @private
	 * @param {Meeting} data The updated meeting
	 */
	_updateMeeting(data) {
		let index = this._meetings.findIndex(meeting => meeting.id === data.id);
		if (index < 0) {
			return;
		}
		this._meetings[index] = new Meeting(this._meetingService, data);
	}

	/**
	 * Remove an existing meeting from the list
	 * @private
	 * @param {string} id ID of the meeting to remove
	 * @return {Meeting} The just removed meeting if any
	 */
	_rmMeeting(id) {
		let index = this._meetings.findIndex(meeting => meeting.id === id);
		if (index < 0) {
			return;
		}
		let meeting = this._meetings.splice(index, 1)[0];
		meeting.off('delete', this.onLocalMeetingRemove);
		return meeting;
	}

	/**
	 * Internal callback for Meeting event
	 * @private
	 * @param {Meeting} meeting
	 */
	onLocalMeetingRemove(meeting) {
		if (!this._rmMeeting(meeting.id)) {
			this.emit('delete', meeting);
		}
	}

	/**
	 * Internal callback for WAC event
	 * @private
	 * @param {Object} eventMeeting
	 */
	onUserInMeeting(eventMeeting) {
		let meeting = this._meetings.find(elem => eventMeeting.id === elem.id);
		if (!meeting) {
			return;
		}
		meeting.phone = eventMeeting.phone;
		if (meeting.phone !== '') {
			this.emit('participant-connected');
		} else {
			this.emit('participant-disconnected');
		}
	}

	_checkInitializingOfService() {
		if (this._initializing === null) {
			throw new Error('MeetingManager not initialized');
		}
	}

	/**
	 * Get phones that are used to create the meeting
	 * @param  {string[]} participants A list of objects
	 * @return {string[]} A list that contains phones
	 */
	_getPhonesFromParticipants(participants) {
		return participants.map(participant => participant.phone).filter(phone => phone);
	}

}