src/meetings/MeetingManager.ts
import {List} from 'immutable';
import {BehaviorSubject, Observable} from 'rxjs';
import Semaphore from 'semaphore-async-await';
import {MeetingService, Meeting as RawMeeting} from '../wac-proxy/wac-stack/MeetingService';
import {Meeting} from './Meeting';
import {MeetingParticipant} from './MeetingParticipant';
export interface MeetingUpdateData
extends Partial<Pick<Meeting, 'name' | 'validSince' | 'validUntil'>> {
id: string;
participants?: Iterable<MeetingParticipant>;
password?: string;
}
const byValidSince = (a: Meeting, b: Meeting): number => a.validSince - b.validSince;
/**
* ContactManager instance is obtained by calling the {@link Session#getMeetingManager}
* method of {@link Session} and must not be directly instantiated.
*/
export class MeetingManager {
/** @private */
private initLock = new Semaphore(1);
/** @private */
private meetingService: MeetingService;
/** @private */
private meetingsSubject = new BehaviorSubject(List<Meeting>());
/** @private */
private meetings$ = this.meetingsSubject.asObservable();
/** @ignore */
constructor(meetingService: MeetingService) {
/** @private */
this.meetingService = meetingService;
this.onMeeting = this.onMeeting.bind(this);
this.onMeetingDelete = this.onMeetingDelete.bind(this);
}
/** @ignore */
async init(): Promise<void> {
await this.initLock.wait();
this.meetingService.emitter.on('create', this.onMeeting);
this.meetingService.emitter.on('delete', this.onMeetingDelete);
this.meetingService.emitter.on('update', this.onMeeting);
const rawMeetings = await this.meetingService.get();
const meetings = List(rawMeetings)
.map(m => new Meeting(m))
.sort(byValidSince);
this.meetingsSubject.next(meetings);
this.initLock.release();
}
/** @ignore */
async uninit(): Promise<void> {
await this.initLock.wait();
this.meetingService.emitter.off('create', this.onMeeting);
this.meetingService.emitter.off('delete', this.onMeetingDelete);
this.meetingService.emitter.off('update', this.onMeeting);
this.meetingsSubject.next(List());
this.initLock.release();
}
/**
* Retrieves a list with the available meetings
* @return {Observable<ImmutableList<Meeting>>}
*/
getMeetings$(): Observable<List<Meeting>> {
return this.meetings$;
}
/**
* Retrieves a meeting with the following id
* @param {string} meetingId Id of the meeeting to retrieve
* @return {Observable<ImmutableList<Meeting>>}
*/
async getMeeting(meetingId: string): Promise<Meeting> {
const meeting = await this.meetingService.getById(meetingId);
return new Meeting(meeting);
}
/**
* Creates a meeting
* @param {string} name The name of the meeting.
* @param {Iterable<MeetingParticipant>} participants The participants of the new meeting session.
* @param {number} validSince The timestamp of the moment when the meeting starts.
* @param {number} [validUntil] The timestamp until the meeting is valid.
* @param {boolean} [sendSms=false] If true, an invitation for the meeting will be sent in an SMS by the server.
* @param {string} [password] Password for meeting access protection
* @return {Promise<Meeting>} The just created meeting
*/
async createMeeting(
name: string,
participants: Iterable<MeetingParticipant>,
validSince: number,
validUntil?: number,
sendSms = false,
password?: string,
): Promise<Meeting> {
const meetingData = await this.meetingService.create(
{
name,
participants: [...participants],
validSince,
validUntil,
password,
},
sendSms,
);
const meeting = new Meeting(meetingData);
this.notifyMeeting(meeting);
return meeting;
}
/**
* Updates a meeting
* @param {Meeting} meeting
* @param {boolean} [sendSms=false] Flag used to decide whether to send an SMS message or not
* @return {Promise<Meeting>}
*/
async saveMeeting(meeting: MeetingUpdateData, sendSms = false): Promise<Meeting> {
const participants = meeting.participants ? {participants: [...meeting.participants]} : {};
const rawMeeting = await this.meetingService.update(
meeting.id,
{
...participants,
name: meeting.name,
validSince: meeting.validSince,
validUntil: meeting.validUntil,
password: meeting.password,
},
sendSms,
);
const newMeeting = new Meeting(rawMeeting);
this.notifyMeeting(newMeeting);
return newMeeting;
}
/**
* Removes a meeting given its id
* @param {object} meeting
* @param {string} meeting.id
*/
async removeMeeting({id}: Pick<Meeting, 'id'>): Promise<void> {
await this.meetingService.delete(id);
const meetings = this.meetingsSubject.value.filter(m => m.id !== id);
this.meetingsSubject.next(meetings);
}
/** @private */
private onMeeting(rawMeeting: RawMeeting): void {
const meeting = new Meeting(rawMeeting);
this.notifyMeeting(meeting);
}
/** @private */
private onMeetingDelete({id}: {id: string}): void {
const meetings = this.meetingsSubject.value;
const updatedMeetings = meetings.filter(m => m.id !== id);
this.meetingsSubject.next(updatedMeetings);
}
/** @private */
private notifyMeeting(meeting: Meeting): void {
const meetings = this.meetingsSubject.value;
const index = meetings.findIndex(m => m.id === meeting.id);
const updatedMeetings = index < 0 ? meetings.push(meeting) : meetings.set(index, meeting);
const sortedMeetings = updatedMeetings.sort(byValidSince);
this.meetingsSubject.next(sortedMeetings);
}
}