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.ts 9.6KB

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