Home Reference Source

src/calls/CallLog.js

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

import {CallDirection} from './CallDirection';
import {getEndReasonFromWacValue} from './CallEndReason';
import {CallLogEntry} from './CallLogEntry';
import {CallStatus} from './CallStatus';

const log = Logs.instance.getLogger('SippoJS/CallLog');

/**
 * Manages the contacts and groups which they belongs to
 * CallLogManager instance is obtained by calling the {Session#getCallLogManager} method
 */
export class CallLog {
	/**
	 * @protected
	 */
	static create(callService, userManager) {
		const callLog = new CallLog(callService, userManager);
		return callLog.fetch().then(() => callLog);
	}

	/**
	 * @private
	 */
	constructor(callService, userManager) {
		/** @private */
		this.callService = callService;
		/** @private */
		this.userManager = userManager;
		/** @private */
		this.entries = [];
		/** @private */
		this.lastEntryRecovered = false;

		/**
		 * Emits the next possible events:
		 * - "update" event every time an existing entry is updated
		 * - "create" event every time a new entry is prepended
		 * - "fetch" event every time more entries are appended
		 * @type {EventEmitter}
		*/
		this.emitter = new EventEmitter();

		bindMethods(this, [
			'onUpdate',
		]);
		this.bindEventHandlers();
	}

	/**
	 * @private
	 */
	bindEventHandlers() {
		this.callService.emitter.on('updated', this.onUpdate);
		this.callService.emitter.on('created', this.onUpdate);
	}

	/**
	 * Fetch more callLog entries
	 * @param {number} [limit=20] The number of new entries to be fetched at most
	 * @return {Promise} When resolved, the update was completed
	 */
	fetch(limit = 20) {
		if (this.lastEntryRecovered) {
			return;
		}
		return this.callService.get({limit, sortBy: 'from'}).then((data) => {
			if (data.length <= 0) {
				this.lastEntryRecovered = true;
				return;
			}
			this.lastEntryRecovered = true; // Hack until lastRecoveredTimestamp is used
			this.lastRecoveredTimestamp = data[data.length - 1].from;
			const callLogs = data.map(datum => this.createCallLogEntry(datum));
			this.entries = [...this.entries, ...callLogs];
			this.emitter.emit('fetch');
		});
	}

	/**
	 * Obtains the list with the available CallLog items
	 * @return {CallLog[]}
	 */
	getEntries() {
		return this.entries;
	}

	/**
	 * Retrieves a CallLog by given its ID
	 * @param {String} id
	 * @return {CallLog}
	 */
	getById(id) {
		return this.entries.find(callLog => callLog.id === id);
	}

	/**
	 * @private
	 */
	calculateStatus(status) {
		switch (status) {
			case 'active':
				return CallStatus.CONNECTED;
			case 'routed':
				return CallStatus.TRYING;
			case 'finished':
				return CallStatus.DISCONNECTED;
			default:
				log.error(`Unexpected status: ${status}`);
		}
	}

	/**
	 * @private
	 */
	createCallLogEntry(datum) {
		const status = this.calculateStatus(datum.state);
		const endReason = getEndReasonFromWacValue(datum.endReason);
		const direction = this.userManager.isOwnAddress(datum.caller) ?
			CallDirection.OUTGOING : CallDirection.INCOMING;

		return new CallLogEntry(
			datum.id,
			datum.callee,
			datum.caller,
			datum.target || datum.callee,
			new Date(datum.from),
			new Date(datum.to),
			status,
			endReason,
			direction,
			datum.origin,
			datum.data,
		);
	}

	/**
 	 * @private
 	 */
	onUpdate(callLogDatum) {
		const newEntry = this.createCallLogEntry(callLogDatum);
		const oldEntry = this.entries.find(entry => entry.id === callLogDatum.id);
		if (oldEntry) {
			this.entries = this.entries.map(entry => entry.getId() === callLogDatum.id ? newEntry : entry);
			this.emitter.emit('update', newEntry, oldEntry);
		} else {
			this.entries = [newEntry, ...this.entries];
			this.emitter.emit('create', newEntry);
		}
	}
}