/* @flow */ import type { Dispatch } from 'redux'; import { conferenceWillLeave } from '../conference'; import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet'; import { CONNECTION_DISCONNECTED, CONNECTION_ESTABLISHED, CONNECTION_FAILED, SET_LOCATION_URL } from './actionTypes'; /** * Opens new connection. * * @returns {Function} */ export function connect() { return (dispatch: Dispatch<*>, getState: Function) => { const state = getState(); const { options } = state['features/base/connection']; const { issuer, jwt } = state['features/jwt']; const { room } = state['features/base/conference']; const connection = new JitsiMeetJS.JitsiConnection( options.appId, jwt && issuer && issuer !== 'anonymous' ? jwt : undefined, { ...options, bosh: options.bosh // XXX The Jitsi Meet deployments require the room // argument to be in lower case at the time of this // writing but, unfortunately, they do not ignore // case themselves. + (room ? `?room=${room.toLowerCase()}` : '') }); connection.addEventListener( JitsiConnectionEvents.CONNECTION_DISCONNECTED, _onConnectionDisconnected); connection.addEventListener( JitsiConnectionEvents.CONNECTION_ESTABLISHED, _onConnectionEstablished); connection.addEventListener( JitsiConnectionEvents.CONNECTION_FAILED, _onConnectionFailed); connection.connect(); /** * Dispatches CONNECTION_DISCONNECTED action when connection is * disconnected. * * @param {string} message - Disconnect reason. * @returns {void} * @private */ function _onConnectionDisconnected(message: string) { connection.removeEventListener( JitsiConnectionEvents.CONNECTION_DISCONNECTED, _onConnectionDisconnected); dispatch(_connectionDisconnected(connection, message)); } /** * Resolves external promise when connection is established. * * @returns {void} * @private */ function _onConnectionEstablished() { unsubscribe(); dispatch(connectionEstablished(connection)); } /** * Rejects external promise when connection fails. * * @param {JitsiConnectionErrors} err - Connection error. * @returns {void} * @private */ function _onConnectionFailed(err) { unsubscribe(); console.error('CONNECTION FAILED:', err); dispatch(connectionFailed(connection, err)); } /** * Unsubscribes connection instance from CONNECTION_ESTABLISHED * and CONNECTION_FAILED events. * * @returns {void} */ function unsubscribe() { connection.removeEventListener( JitsiConnectionEvents.CONNECTION_ESTABLISHED, _onConnectionEstablished); connection.removeEventListener( JitsiConnectionEvents.CONNECTION_FAILED, _onConnectionFailed); } }; } /** * Create an action for when the signaling connection has been lost. * * @param {JitsiConnection} connection - The JitsiConnection which disconnected. * @param {string} message - Error message. * @private * @returns {{ * type: CONNECTION_DISCONNECTED, * connection: JitsiConnection, * message: string * }} */ function _connectionDisconnected(connection: Object, message: string) { return { type: CONNECTION_DISCONNECTED, connection, message }; } /** * Create an action for when the signaling connection has been established. * * @param {JitsiConnection} connection - The JitsiConnection which was * established. * @public * @returns {{ * type: CONNECTION_ESTABLISHED, * connection: JitsiConnection * }} */ export function connectionEstablished(connection: Object) { return { type: CONNECTION_ESTABLISHED, connection }; } /** * Create an action for when the signaling connection could not be created. * * @param {JitsiConnection} connection - The JitsiConnection which failed. * @param {string} error - Error. * @param {string} message - Error message. * @public * @returns {{ * type: CONNECTION_FAILED, * connection: JitsiConnection, * error: string, * message: string * }} */ export function connectionFailed( connection: Object, error: string, message: ?string) { return { type: CONNECTION_FAILED, connection, error, message }; } /** * Closes connection. * * @returns {Function} */ export function disconnect() { return (dispatch: Dispatch<*>, getState: Function) => { const state = getState(); const { conference } = state['features/base/conference']; const { connection } = state['features/base/connection']; let promise; // Leave the conference. if (conference) { // In a fashion similar to JitsiConference's CONFERENCE_LEFT event // (and the respective Redux action) which is fired after the // conference has been left, notify the application about the // intention to leave the conference. dispatch(conferenceWillLeave(conference)); promise = conference.leave(); } else { promise = Promise.resolve(); } // Disconnect the connection. if (connection) { promise = promise.then(() => connection.disconnect()); } return promise; }; } /** * Sets the location URL of the application, connecton, conference, etc. * * @param {URL} [locationURL] - The location URL of the application, * connection, conference, etc. * @returns {{ * type: SET_LOCATION_URL, * locationURL: URL * }} */ export function setLocationURL(locationURL: ?URL) { return { type: SET_LOCATION_URL, locationURL }; }