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.

middleware.any.js 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // @flow
  2. import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
  3. import {
  4. CONFERENCE_JOINED
  5. } from '../base/conference';
  6. import {
  7. getLocalParticipant,
  8. getParticipantCount,
  9. getParticipantPresenceStatus,
  10. getRemoteParticipants,
  11. PARTICIPANT_JOINED,
  12. PARTICIPANT_JOINED_SOUND_ID,
  13. PARTICIPANT_LEFT,
  14. PARTICIPANT_UPDATED,
  15. pinParticipant
  16. } from '../base/participants';
  17. import { MiddlewareRegistry } from '../base/redux';
  18. import {
  19. playSound,
  20. registerSound,
  21. stopSound,
  22. unregisterSound
  23. } from '../base/sounds';
  24. import {
  25. CALLING,
  26. CONNECTED_USER,
  27. EXPIRED,
  28. INVITED,
  29. REJECTED,
  30. RINGING
  31. } from '../presence-status';
  32. import {
  33. SET_CALLEE_INFO_VISIBLE,
  34. UPDATE_DIAL_IN_NUMBERS_FAILED
  35. } from './actionTypes';
  36. import {
  37. invite,
  38. removePendingInviteRequests,
  39. setCalleeInfoVisible
  40. } from './actions';
  41. import {
  42. OUTGOING_CALL_EXPIRED_SOUND_ID,
  43. OUTGOING_CALL_REJECTED_SOUND_ID,
  44. OUTGOING_CALL_RINGING_SOUND_ID,
  45. OUTGOING_CALL_START_SOUND_ID
  46. } from './constants';
  47. import logger from './logger';
  48. import { sounds } from './sounds';
  49. declare var interfaceConfig: Object;
  50. /**
  51. * Maps the presence status with the ID of the sound that will be played when
  52. * the status is received.
  53. */
  54. const statusToRingtone = {
  55. [CALLING]: OUTGOING_CALL_START_SOUND_ID,
  56. [CONNECTED_USER]: PARTICIPANT_JOINED_SOUND_ID,
  57. [EXPIRED]: OUTGOING_CALL_EXPIRED_SOUND_ID,
  58. [INVITED]: OUTGOING_CALL_START_SOUND_ID,
  59. [REJECTED]: OUTGOING_CALL_REJECTED_SOUND_ID,
  60. [RINGING]: OUTGOING_CALL_RINGING_SOUND_ID
  61. };
  62. /**
  63. * The middleware of the feature invite common to mobile/react-native and
  64. * Web/React.
  65. *
  66. * @param {Store} store - The redux store.
  67. * @returns {Function}
  68. */
  69. MiddlewareRegistry.register(store => next => action => {
  70. let oldParticipantPresence;
  71. const { dispatch, getState } = store;
  72. const state = getState();
  73. if (action.type === PARTICIPANT_UPDATED
  74. || action.type === PARTICIPANT_LEFT) {
  75. oldParticipantPresence
  76. = getParticipantPresenceStatus(state, action.participant.id);
  77. }
  78. if (action.type === SET_CALLEE_INFO_VISIBLE) {
  79. if (action.calleeInfoVisible) {
  80. dispatch(pinParticipant(getLocalParticipant(state).id));
  81. } else {
  82. // unpin participant
  83. dispatch(pinParticipant());
  84. }
  85. }
  86. const result = next(action);
  87. switch (action.type) {
  88. case APP_WILL_MOUNT:
  89. for (const [ soundId, sound ] of sounds.entries()) {
  90. dispatch(registerSound(soundId, sound.file, sound.options));
  91. }
  92. break;
  93. case APP_WILL_UNMOUNT:
  94. for (const soundId of sounds.keys()) {
  95. dispatch(unregisterSound(soundId));
  96. }
  97. break;
  98. case CONFERENCE_JOINED:
  99. _onConferenceJoined(store);
  100. break;
  101. case PARTICIPANT_JOINED:
  102. case PARTICIPANT_LEFT:
  103. case PARTICIPANT_UPDATED: {
  104. _maybeHideCalleeInfo(action, store);
  105. const newParticipantPresence
  106. = getParticipantPresenceStatus(state, action.participant.id);
  107. if (oldParticipantPresence === newParticipantPresence) {
  108. break;
  109. }
  110. const oldSoundId
  111. = oldParticipantPresence
  112. && statusToRingtone[oldParticipantPresence];
  113. const newSoundId
  114. = newParticipantPresence
  115. && statusToRingtone[newParticipantPresence];
  116. if (oldSoundId === newSoundId) {
  117. break;
  118. }
  119. if (oldSoundId) {
  120. dispatch(stopSound(oldSoundId));
  121. }
  122. if (newSoundId) {
  123. dispatch(playSound(newSoundId));
  124. }
  125. break;
  126. }
  127. case UPDATE_DIAL_IN_NUMBERS_FAILED:
  128. logger.error(
  129. 'Error encountered while fetching dial-in numbers:',
  130. action.error);
  131. break;
  132. }
  133. return result;
  134. });
  135. /**
  136. * Hides the callee info layot if there are more than 1 real
  137. * (not poltergeist, shared video, etc.) participants in the call.
  138. *
  139. * @param {Object} action - The redux action.
  140. * @param {ReduxStore} store - The redux store.
  141. * @returns {void}
  142. */
  143. function _maybeHideCalleeInfo(action, store) {
  144. const state = store.getState();
  145. if (!state['features/invite'].calleeInfoVisible) {
  146. return;
  147. }
  148. const participants = getRemoteParticipants(state);
  149. const participantCount = getParticipantCount(state);
  150. let numberOfPoltergeists = 0;
  151. participants.forEach(p => {
  152. if (p.botType === 'poltergeist') {
  153. numberOfPoltergeists++;
  154. }
  155. });
  156. const numberOfRealParticipants = participantCount - numberOfPoltergeists;
  157. if ((numberOfPoltergeists > 1 || numberOfRealParticipants > 1)
  158. || (action.type === PARTICIPANT_LEFT && participantCount === 1)) {
  159. store.dispatch(setCalleeInfoVisible(false));
  160. }
  161. }
  162. /**
  163. * Executes the pending invitation requests if any.
  164. *
  165. * @param {ReduxStore} store - The redux store.
  166. * @returns {void}
  167. */
  168. function _onConferenceJoined(store) {
  169. const { dispatch, getState } = store;
  170. const pendingInviteRequests
  171. = getState()['features/invite'].pendingInviteRequests || [];
  172. pendingInviteRequests.forEach(({ invitees, callback }) => {
  173. dispatch(invite(invitees))
  174. .then(failedInvitees => {
  175. callback(failedInvitees);
  176. });
  177. });
  178. dispatch(removePendingInviteRequests());
  179. }