Home Reference Source

src/groups/GroupManager.js

import {List} from 'immutable';
import {firstValueFrom} from 'rxjs';

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

import {Group} from './Group';

function comparator(a, b) {
	if (a.name < b.name) {
		return -1;
	}
	if (a.name > b.name) {
		return 1;
	}
	return 0;
}

/**
 * This class allows manager the groups. To obtain an instance of the
 * class @link {Session#getGroupManager} method must be used.
 *
 * # Events
 * - `groups` - Emitted every time a group list changes.
 */
export class GroupManager {
	/** @protected */
	static newInstance(groupService, phonebookService, userRepository) {
		const groupManager = new GroupManager(groupService, phonebookService, userRepository);
		return groupManager.init();
	}

	/** @private */
	constructor(groupService, phonebookService, userRepository) {
		/** @private */
		this.groupService = groupService;
		/** @private */
		this.phonebookService = phonebookService;
		/** @private */
		this.userRepository = userRepository;
		/** @private */
		this.groups = List();
		/** @type {EventEmitter} */
		this.emitter = new EventEmitter();
		bindMethods(this, [
			'onDelete',
			'onPut',
		]);
	}

	/** @private */
	async init() {
		this.groupService.emitter.on('put', this.onPut);
		this.groupService.emitter.on('delete', this.onDelete);
		const groups = await this.groupService.fetch();
		for (const group of groups) {
			group.participants = await this.convertToAddresses(group.participants);
		}
		this.groups = List.of(...await Promise.all(groups.map(datum => this.newGroup(datum))));
		this.groups = this.groups.sort(comparator);
		return this;
	}

	/** @protected */
	uninit() {
		this.groupService.emitter.off('put', this.onPut);
		this.groupService.emitter.off('delete', this.onDelete);
	}

	/** @private */
	async convertToAddresses(ids) {
		const participants = [];
		for (const id of ids) {
			const user = await firstValueFrom(this.userRepository.getUser$(id));
			participants.push(`${user.username}@${user.domain}`);
		}
		return participants;
	}

	/** @private */
	async convertToIds(participants) {
		const ids = [];
		for (const participant of participants) {
			const user = await firstValueFrom(this.userRepository.getUserByAddress$(participant));
			ids.push(user.id);
		}
		return ids;
	}

	/** @private */
	async onPut(event) {
		event.participants = await this.convertToAddresses(event.participants);
		const index = this.groups.findIndex(group => group.getId() === event.id);
		const group = await this.newGroup(event);
		if (index < 0) {
			this.groups = this.groups.push(group);
		} else {
			this.groups = this.groups.set(index, group);
		}
		this.groups = this.groups.sort(comparator);
		this.emitter.emit('groups');
		return group;
	}

	/** @private */
	onDelete({id}) {
		this.groups = this.groups.filter(group => group.getId() !== id);
		this.emitter.emit('groups');
	}

	/** @private */
	async newGroup({id, name, participants}) {
		const res = await this.phonebookService.getPhonebooksBySubscriberId(id);
		const phonebook = res.map(p => p.contacts).reduce((acc, value) => acc.concat(value), []);
		return Group.of({id, name, participants, phonebook});
	}

	/**
	 * Allows access to the list of groups
	 * @return {ImmutableList<Group>}
	 */
	getGroups() {
		return this.groups;
	}

	/**
	 * Creates a new group
	 * @param {string} name The name of the new group
	 * @param {string[]} participants List of the addresses of the initial participants
	 * @return {Promise<Group>}
	 */
	async createGroup(name, participants) {
		const participantIds = await this.convertToIds(participants);
		const datum = await this.groupService.create(name, participantIds);
		const group = await this.onPut(datum);
		return group;
	}

	/**
	 * Saves a group (Still no WAC support)
	 * @experimental
	 * @param {Group} group The group to be persisted
	 * @return {Promise}
	 */
	async saveGroup(group) {
		const participantIds = await this.convertToIds(group.getParticipants().toJS());
		await this.groupService.update(group.getId(), group.getName(), participantIds);
	}

	/**
	 * Deletes a group
	 * @param {Group} group The group to be deleted
	 * @return {Promise}
	 */
	async deleteGroup(group) {
		await this.groupService.delete(group.getId());
		await this.onDelete({id: group.getId()});
	}

	/**
	 * Obtain phonebooks asociated with a group. Phonebooks are a list of contacts
	 * many groups can susbscribe to.
	 *
	 * @return {Array} containing the phonebooks asociated to the group
	 * @property {string[]} subscribers ids of the group subscribers
	 * @property {string} name phonebookName
	 * @property {string} id phonebookId
	 * @property {Object[]} contacts list of ponebook contacts
	 * @property {string} contacts.name contact name
	 * @property {string} contacts.address contact address
	 * @deprecated
	 */
	getPhonebook(group) {
		return this.phonebookService.getPhonebooksBySubscriberId(group.getId());
	}
}