You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

actions.any.js 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // @flow
  2. import type { Dispatch } from 'redux';
  3. import { getInviteURL } from '../base/connection';
  4. import { getLocalParticipant, getParticipantCount } from '../base/participants';
  5. import { inviteVideoRooms } from '../videosipgw';
  6. import {
  7. ADD_PENDING_INVITE_REQUEST,
  8. BEGIN_ADD_PEOPLE,
  9. HIDE_ADD_PEOPLE_DIALOG,
  10. REMOVE_PENDING_INVITE_REQUESTS,
  11. SET_CALLEE_INFO_VISIBLE,
  12. UPDATE_DIAL_IN_NUMBERS_FAILED,
  13. UPDATE_DIAL_IN_NUMBERS_SUCCESS
  14. } from './actionTypes';
  15. import { INVITE_TYPES } from './constants';
  16. import {
  17. getDialInConferenceID,
  18. getDialInNumbers,
  19. invitePeopleAndChatRooms,
  20. inviteSipEndpoints
  21. } from './functions';
  22. import logger from './logger';
  23. /**
  24. * Creates a (redux) action to signal that a click/tap has been performed on
  25. * {@link InviteButton} and that the execution flow for adding/inviting people
  26. * to the current conference/meeting is to begin.
  27. *
  28. * @returns {{
  29. * type: BEGIN_ADD_PEOPLE
  30. * }}
  31. */
  32. export function beginAddPeople() {
  33. return {
  34. type: BEGIN_ADD_PEOPLE
  35. };
  36. }
  37. /**
  38. * Creates a (redux) action to signal that the {@code AddPeopleDialog}
  39. * should close.
  40. *
  41. * @returns {{
  42. * type: HIDE_ADD_PEOPLE_DIALOG
  43. * }}
  44. */
  45. export function hideAddPeopleDialog() {
  46. return {
  47. type: HIDE_ADD_PEOPLE_DIALOG
  48. };
  49. }
  50. /**
  51. * Invites (i.e. Sends invites to) an array of invitees (which may be a
  52. * combination of users, rooms, phone numbers, and video rooms.
  53. *
  54. * @param {Array<Object>} invitees - The recipients to send invites to.
  55. * @param {Array<Object>} showCalleeInfo - Indicates whether the
  56. * {@code CalleeInfo} should be displayed or not.
  57. * @returns {Promise<Array<Object>>} A {@code Promise} resolving with an array
  58. * of invitees who were not invited (i.e. Invites were not sent to them).
  59. */
  60. export function invite(
  61. invitees: Array<Object>,
  62. showCalleeInfo: boolean = false) {
  63. return (
  64. dispatch: Dispatch<any>,
  65. getState: Function): Promise<Array<Object>> => {
  66. const state = getState();
  67. const participantsCount = getParticipantCount(state);
  68. const { calleeInfoVisible } = state['features/invite'];
  69. if (showCalleeInfo
  70. && !calleeInfoVisible
  71. && invitees.length === 1
  72. && invitees[0].type === INVITE_TYPES.USER
  73. && participantsCount === 1) {
  74. dispatch(setCalleeInfoVisible(true, invitees[0]));
  75. }
  76. const { conference, password } = state['features/base/conference'];
  77. if (typeof conference === 'undefined') {
  78. // Invite will fail before CONFERENCE_JOIN. The request will be
  79. // cached in order to be executed on CONFERENCE_JOIN.
  80. return new Promise(resolve => {
  81. dispatch(addPendingInviteRequest({
  82. invitees,
  83. callback: failedInvitees => resolve(failedInvitees)
  84. }));
  85. });
  86. }
  87. let allInvitePromises = [];
  88. let invitesLeftToSend = [ ...invitees ];
  89. const {
  90. callFlowsEnabled,
  91. inviteServiceUrl,
  92. inviteServiceCallFlowsUrl
  93. } = state['features/base/config'];
  94. const inviteUrl = getInviteURL(state);
  95. const { sipInviteUrl } = state['features/base/config'];
  96. const { locationURL } = state['features/base/connection'];
  97. const { jwt } = state['features/base/jwt'];
  98. const { name: displayName } = getLocalParticipant(state);
  99. // First create all promises for dialing out.
  100. const phoneNumbers
  101. = invitesLeftToSend.filter(({ type }) => type === INVITE_TYPES.PHONE);
  102. // For each number, dial out. On success, remove the number from
  103. // {@link invitesLeftToSend}.
  104. const phoneInvitePromises = phoneNumbers.map(item => {
  105. const numberToInvite = item.number;
  106. return conference.dial(numberToInvite)
  107. .then(() => {
  108. invitesLeftToSend
  109. = invitesLeftToSend.filter(
  110. invitee => invitee !== item);
  111. })
  112. .catch(error =>
  113. logger.error('Error inviting phone number:', error));
  114. });
  115. allInvitePromises = allInvitePromises.concat(phoneInvitePromises);
  116. const usersAndRooms
  117. = invitesLeftToSend.filter(
  118. ({ type }) => [ INVITE_TYPES.USER, INVITE_TYPES.ROOM ].includes(type));
  119. if (usersAndRooms.length) {
  120. // Send a request to invite all the rooms and users. On success,
  121. // filter all rooms and users from {@link invitesLeftToSend}.
  122. const peopleInvitePromise
  123. = invitePeopleAndChatRooms(
  124. callFlowsEnabled
  125. ? inviteServiceCallFlowsUrl : inviteServiceUrl,
  126. inviteUrl,
  127. jwt,
  128. usersAndRooms)
  129. .then(() => {
  130. invitesLeftToSend
  131. = invitesLeftToSend.filter(
  132. ({ type }) => ![ INVITE_TYPES.USER, INVITE_TYPES.ROOM ].includes(type));
  133. })
  134. .catch(error => {
  135. dispatch(setCalleeInfoVisible(false));
  136. logger.error('Error inviting people:', error);
  137. });
  138. allInvitePromises.push(peopleInvitePromise);
  139. }
  140. // Sipgw calls are fire and forget. Invite them to the conference, then
  141. // immediately remove them from invitesLeftToSend.
  142. const vrooms
  143. = invitesLeftToSend.filter(({ type }) => type === INVITE_TYPES.VIDEO_ROOM);
  144. conference
  145. && vrooms.length > 0
  146. && dispatch(inviteVideoRooms(conference, vrooms));
  147. invitesLeftToSend
  148. = invitesLeftToSend.filter(({ type }) => type !== INVITE_TYPES.VIDEO_ROOM);
  149. const sipEndpoints
  150. = invitesLeftToSend.filter(({ type }) => type === INVITE_TYPES.SIP);
  151. conference && inviteSipEndpoints(
  152. sipEndpoints,
  153. locationURL,
  154. sipInviteUrl,
  155. jwt,
  156. conference.options.name,
  157. password,
  158. displayName
  159. );
  160. invitesLeftToSend
  161. = invitesLeftToSend.filter(({ type }) => type !== INVITE_TYPES.SIP);
  162. return (
  163. Promise.all(allInvitePromises)
  164. .then(() => invitesLeftToSend));
  165. };
  166. }
  167. /**
  168. * Sends AJAX requests for dial-in numbers and conference ID.
  169. *
  170. * @returns {Function}
  171. */
  172. export function updateDialInNumbers() {
  173. return (dispatch: Dispatch<any>, getState: Function) => {
  174. const state = getState();
  175. const { dialInConfCodeUrl, dialInNumbersUrl, hosts }
  176. = state['features/base/config'];
  177. const { numbersFetched } = state['features/invite'];
  178. const mucURL = hosts && hosts.muc;
  179. if (numbersFetched || !dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
  180. // URLs for fetching dial in numbers not defined
  181. return;
  182. }
  183. const { room } = state['features/base/conference'];
  184. Promise.all([
  185. getDialInNumbers(dialInNumbersUrl, room, mucURL),
  186. getDialInConferenceID(dialInConfCodeUrl, room, mucURL)
  187. ])
  188. .then(([ dialInNumbers, { conference, id, message, sipUri } ]) => {
  189. if (!conference || !id) {
  190. return Promise.reject(message);
  191. }
  192. dispatch({
  193. type: UPDATE_DIAL_IN_NUMBERS_SUCCESS,
  194. conferenceID: id,
  195. dialInNumbers,
  196. sipUri
  197. });
  198. })
  199. .catch(error => {
  200. dispatch({
  201. type: UPDATE_DIAL_IN_NUMBERS_FAILED,
  202. error
  203. });
  204. });
  205. };
  206. }
  207. /**
  208. * Sets the visibility of {@code CalleeInfo}.
  209. *
  210. * @param {boolean|undefined} [calleeInfoVisible] - If {@code CalleeInfo} is
  211. * to be displayed/visible, then {@code true}; otherwise, {@code false} or
  212. * {@code undefined}.
  213. * @param {Object|undefined} [initialCalleeInfo] - Callee information.
  214. * @returns {{
  215. * type: SET_CALLEE_INFO_VISIBLE,
  216. * calleeInfoVisible: (boolean|undefined),
  217. * initialCalleeInfo
  218. * }}
  219. */
  220. export function setCalleeInfoVisible(
  221. calleeInfoVisible: boolean,
  222. initialCalleeInfo: ?Object) {
  223. return {
  224. type: SET_CALLEE_INFO_VISIBLE,
  225. calleeInfoVisible,
  226. initialCalleeInfo
  227. };
  228. }
  229. /**
  230. * Adds pending invite request.
  231. *
  232. * @param {Object} request - The request.
  233. * @returns {{
  234. * type: ADD_PENDING_INVITE_REQUEST,
  235. * request: Object
  236. * }}
  237. */
  238. export function addPendingInviteRequest(
  239. request: { invitees: Array<Object>, callback: Function }) {
  240. return {
  241. type: ADD_PENDING_INVITE_REQUEST,
  242. request
  243. };
  244. }
  245. /**
  246. * Removes all pending invite requests.
  247. *
  248. * @returns {{
  249. * type: REMOVE_PENDING_INVITE_REQUEST
  250. * }}
  251. */
  252. export function removePendingInviteRequests() {
  253. return {
  254. type: REMOVE_PENDING_INVITE_REQUESTS
  255. };
  256. }