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 8.1KB

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