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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // @flow
  2. import { getCurrentConference } from '../base/conference';
  3. import { VIDEO_TYPE } from '../base/media';
  4. import {
  5. PARTICIPANT_LEFT,
  6. PIN_PARTICIPANT,
  7. pinParticipant,
  8. getParticipantById,
  9. getPinnedParticipant
  10. } from '../base/participants';
  11. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  12. import { TRACK_REMOVED } from '../base/tracks';
  13. import { SET_DOCUMENT_EDITING_STATUS } from '../etherpad';
  14. import { isFollowMeActive } from '../follow-me';
  15. import { SET_TILE_VIEW } from './actionTypes';
  16. import { setRemoteParticipantsWithScreenShare, setTileView } from './actions';
  17. import { getAutoPinSetting, updateAutoPinnedParticipant } from './functions';
  18. import './subscriber';
  19. let previousTileViewEnabled;
  20. /**
  21. * Middleware which intercepts actions and updates tile view related state.
  22. *
  23. * @param {Store} store - The redux store.
  24. * @returns {Function}
  25. */
  26. MiddlewareRegistry.register(store => next => action => {
  27. // we want to extract the leaving participant and check its type before actually the participant being removed.
  28. let shouldUpdateAutoPin = false;
  29. switch (action.type) {
  30. case PARTICIPANT_LEFT: {
  31. if (!getAutoPinSetting() || isFollowMeActive(store)) {
  32. break;
  33. }
  34. shouldUpdateAutoPin = getParticipantById(store.getState(), action.participant.id)?.isFakeParticipant;
  35. break;
  36. }
  37. }
  38. const result = next(action);
  39. switch (action.type) {
  40. // Actions that temporarily clear the user preferred state of tile view,
  41. // then re-set it when needed.
  42. case PIN_PARTICIPANT: {
  43. const pinnedParticipant = getPinnedParticipant(store.getState());
  44. if (pinnedParticipant) {
  45. _storeTileViewStateAndClear(store);
  46. } else {
  47. _restoreTileViewState(store);
  48. }
  49. break;
  50. }
  51. case SET_DOCUMENT_EDITING_STATUS:
  52. if (action.editing) {
  53. _storeTileViewStateAndClear(store);
  54. } else {
  55. _restoreTileViewState(store);
  56. }
  57. break;
  58. // Things to update when tile view state changes
  59. case SET_TILE_VIEW:
  60. if (action.enabled && getPinnedParticipant(store)) {
  61. store.dispatch(pinParticipant(null));
  62. }
  63. break;
  64. // Update the remoteScreenShares.
  65. // Because of the debounce in the subscriber which updates the remoteScreenShares we need to handle
  66. // removal of screen shares separatelly here. Otherwise it is possible to have screen sharing
  67. // participant that has already left in the remoteScreenShares array. This can lead to rendering
  68. // a thumbnails for already left participants since the remoteScreenShares array is used for
  69. // building the ordered list of remote participants.
  70. case TRACK_REMOVED: {
  71. const { jitsiTrack } = action.track;
  72. if (jitsiTrack && jitsiTrack.isVideoTrack() && jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP) {
  73. const participantId = jitsiTrack.getParticipantId();
  74. const oldScreenShares = store.getState()['features/video-layout'].remoteScreenShares || [];
  75. const newScreenShares = oldScreenShares.filter(id => id !== participantId);
  76. if (oldScreenShares.length !== newScreenShares.length) { // the participant was removed
  77. store.dispatch(setRemoteParticipantsWithScreenShare(newScreenShares));
  78. updateAutoPinnedParticipant(oldScreenShares, store);
  79. }
  80. }
  81. break;
  82. }
  83. }
  84. if (shouldUpdateAutoPin) {
  85. const screenShares = store.getState()['features/video-layout'].remoteScreenShares || [];
  86. updateAutoPinnedParticipant(screenShares, store);
  87. }
  88. return result;
  89. });
  90. /**
  91. * Set up state change listener to perform maintenance tasks when the conference
  92. * is left or failed.
  93. */
  94. StateListenerRegistry.register(
  95. state => getCurrentConference(state),
  96. (conference, { dispatch }, previousConference) => {
  97. if (conference !== previousConference) {
  98. // conference changed, left or failed...
  99. // Clear tile view state.
  100. dispatch(setTileView());
  101. }
  102. });
  103. /**
  104. * Restores tile view state, if it wasn't updated since then.
  105. *
  106. * @param {Object} store - The Redux Store.
  107. * @returns {void}
  108. */
  109. function _restoreTileViewState({ dispatch, getState }) {
  110. const { tileViewEnabled } = getState()['features/video-layout'];
  111. if (tileViewEnabled === undefined && previousTileViewEnabled !== undefined) {
  112. dispatch(setTileView(previousTileViewEnabled));
  113. }
  114. previousTileViewEnabled = undefined;
  115. }
  116. /**
  117. * Stores the current tile view state and clears it.
  118. *
  119. * @param {Object} store - The Redux Store.
  120. * @returns {void}
  121. */
  122. function _storeTileViewStateAndClear({ dispatch, getState }) {
  123. const { tileViewEnabled } = getState()['features/video-layout'];
  124. if (tileViewEnabled !== undefined) {
  125. previousTileViewEnabled = tileViewEnabled;
  126. dispatch(setTileView(undefined));
  127. }
  128. }