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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // @flow
  2. import { NativeEventEmitter, NativeModules } from 'react-native';
  3. import { SET_AUDIO_ONLY } from '../../base/audio-only';
  4. import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app';
  5. import {
  6. CONFERENCE_FAILED,
  7. CONFERENCE_LEFT,
  8. CONFERENCE_JOINED,
  9. getCurrentConference
  10. } from '../../base/conference';
  11. import { MiddlewareRegistry } from '../../base/redux';
  12. import { _SET_AUDIOMODE_DEVICES, _SET_AUDIOMODE_SUBSCRIPTIONS } from './actionTypes';
  13. import logger from './logger';
  14. const { AudioMode } = NativeModules;
  15. const AudioModeEmitter = new NativeEventEmitter(AudioMode);
  16. /**
  17. * Middleware that captures conference actions and sets the correct audio mode
  18. * based on the type of conference. Audio-only conferences don't use the speaker
  19. * by default, and video conferences do.
  20. *
  21. * @param {Store} store - The redux store.
  22. * @returns {Function}
  23. */
  24. MiddlewareRegistry.register(store => next => action => {
  25. /* eslint-disable no-fallthrough */
  26. switch (action.type) {
  27. case _SET_AUDIOMODE_SUBSCRIPTIONS:
  28. _setSubscriptions(store);
  29. break;
  30. case APP_WILL_UNMOUNT: {
  31. store.dispatch({
  32. type: _SET_AUDIOMODE_SUBSCRIPTIONS,
  33. subscriptions: undefined
  34. });
  35. break;
  36. }
  37. case APP_WILL_MOUNT:
  38. _appWillMount(store);
  39. case CONFERENCE_FAILED: // eslint-disable-line no-fallthrough
  40. case CONFERENCE_LEFT:
  41. /*
  42. * NOTE: We moved the audio mode setting from CONFERENCE_WILL_JOIN to
  43. * CONFERENCE_JOINED because in case of a locked room, the app goes
  44. * through CONFERENCE_FAILED state and gets to CONFERENCE_JOINED only
  45. * after a correct password, so we want to make sure we have the correct
  46. * audio mode set up when we finally get to the conf, but also make sure
  47. * that the app is in the right audio mode if the user leaves the
  48. * conference after the password prompt appears.
  49. */
  50. case CONFERENCE_JOINED:
  51. case SET_AUDIO_ONLY:
  52. return _updateAudioMode(store, next, action);
  53. }
  54. /* eslint-enable no-fallthrough */
  55. return next(action);
  56. });
  57. /**
  58. * Notifies this feature that the action {@link APP_WILL_MOUNT} is being
  59. * dispatched within a specific redux {@code store}.
  60. *
  61. * @param {Store} store - The redux store in which the specified {@code action}
  62. * is being dispatched.
  63. * @private
  64. * @returns {void}
  65. */
  66. function _appWillMount(store) {
  67. const subscriptions = [
  68. AudioModeEmitter.addListener(AudioMode.DEVICE_CHANGE_EVENT, _onDevicesUpdate, store)
  69. ];
  70. store.dispatch({
  71. type: _SET_AUDIOMODE_SUBSCRIPTIONS,
  72. subscriptions
  73. });
  74. }
  75. /**
  76. * Handles audio device changes. The list will be stored on the redux store.
  77. *
  78. * @param {Object} devices - The current list of devices.
  79. * @private
  80. * @returns {void}
  81. */
  82. function _onDevicesUpdate(devices) {
  83. const { dispatch } = this; // eslint-disable-line no-invalid-this
  84. dispatch({
  85. type: _SET_AUDIOMODE_DEVICES,
  86. devices
  87. });
  88. }
  89. /**
  90. * Notifies this feature that the action
  91. * {@link _SET_AUDIOMODE_SUBSCRIPTIONS} is being dispatched within
  92. * a specific redux {@code store}.
  93. *
  94. * @param {Store} store - The redux store in which the specified {@code action}
  95. * is being dispatched.
  96. * @private
  97. * @returns {void}
  98. */
  99. function _setSubscriptions({ getState }) {
  100. const { subscriptions } = getState()['features/mobile/audio-mode'];
  101. if (subscriptions) {
  102. for (const subscription of subscriptions) {
  103. subscription.remove();
  104. }
  105. }
  106. }
  107. /**
  108. * Updates the audio mode based on the current (redux) state.
  109. *
  110. * @param {Store} store - The redux store in which the specified {@code action}
  111. * is being dispatched.
  112. * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
  113. * specified {@code action} in the specified {@code store}.
  114. * @param {Action} action - The redux action which is
  115. * being dispatched in the specified {@code store}.
  116. * @private
  117. * @returns {*} The value returned by {@code next(action)}.
  118. */
  119. function _updateAudioMode({ getState }, next, action) {
  120. const result = next(action);
  121. const state = getState();
  122. const conference = getCurrentConference(state);
  123. const { enabled: audioOnly } = state['features/base/audio-only'];
  124. let mode;
  125. if (conference) {
  126. mode = audioOnly ? AudioMode.AUDIO_CALL : AudioMode.VIDEO_CALL;
  127. } else {
  128. mode = AudioMode.DEFAULT;
  129. }
  130. AudioMode.setMode(mode).catch(err => logger.error(`Failed to set audio mode ${String(mode)}: ${err}`));
  131. return result;
  132. }