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.js 8.7KB

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