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.

subscriber.ts 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import _ from 'lodash';
  2. import { batch } from 'react-redux';
  3. import { IStore } from '../../app/types';
  4. import { hideNotification, showNotification } from '../../notifications/actions';
  5. import { NOTIFICATION_TIMEOUT_TYPE, RAISE_HAND_NOTIFICATION_ID } from '../../notifications/constants';
  6. import { getCurrentConference } from '../conference/functions';
  7. import {
  8. getDisableNextSpeakerNotification,
  9. getSsrcRewritingFeatureFlag,
  10. hasBeenNotified,
  11. isNextToSpeak } from '../config/functions.any';
  12. import { VIDEO_TYPE } from '../media/constants';
  13. import StateListenerRegistry from '../redux/StateListenerRegistry';
  14. import { NOTIFIED_TO_SPEAK } from './actionTypes';
  15. import { createVirtualScreenshareParticipant, participantLeft } from './actions';
  16. import {
  17. getParticipantById,
  18. getRemoteScreensharesBasedOnPresence,
  19. getVirtualScreenshareParticipantOwnerId
  20. } from './functions';
  21. import { FakeParticipant } from './types';
  22. StateListenerRegistry.register(
  23. /* selector */ state => state['features/base/tracks'],
  24. /* listener */(tracks, store) => _updateScreenshareParticipants(store)
  25. );
  26. StateListenerRegistry.register(
  27. /* selector */ state => state['features/base/participants'].remoteVideoSources,
  28. /* listener */(remoteVideoSources, store) => getSsrcRewritingFeatureFlag(store.getState())
  29. && _updateScreenshareParticipantsBasedOnPresence(store)
  30. );
  31. StateListenerRegistry.register(
  32. /* selector */ state => state['features/base/participants'].raisedHandsQueue,
  33. /* listener */ (raisedHandsQueue, store) => {
  34. if (raisedHandsQueue.length
  35. && isNextToSpeak(store.getState())
  36. && !hasBeenNotified(store.getState())
  37. && !getDisableNextSpeakerNotification(store.getState())) {
  38. _notifyNextSpeakerInRaisedHandQueue(store);
  39. }
  40. if (!raisedHandsQueue[0]) {
  41. store.dispatch(hideNotification(RAISE_HAND_NOTIFICATION_ID));
  42. }
  43. }
  44. );
  45. /**
  46. * Compares the old and new screenshare lists provided and creates/removes the virtual screenshare participant
  47. * tiles accodingly.
  48. *
  49. * @param {Array<string>} oldScreenshareSourceNames - List of old screenshare source names.
  50. * @param {Array<string>} newScreenshareSourceNames - Current list of screenshare source names.
  51. * @param {Object} store - The redux store.
  52. * @returns {void}
  53. */
  54. function _createOrRemoveVirtualParticipants(
  55. oldScreenshareSourceNames: string[],
  56. newScreenshareSourceNames: string[],
  57. store: IStore): void {
  58. const { dispatch, getState } = store;
  59. const conference = getCurrentConference(getState());
  60. const removedScreenshareSourceNames = _.difference(oldScreenshareSourceNames, newScreenshareSourceNames);
  61. const addedScreenshareSourceNames = _.difference(newScreenshareSourceNames, oldScreenshareSourceNames);
  62. if (removedScreenshareSourceNames.length) {
  63. removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, {
  64. fakeParticipant: FakeParticipant.RemoteScreenShare
  65. })));
  66. }
  67. if (addedScreenshareSourceNames.length) {
  68. addedScreenshareSourceNames.forEach(id => dispatch(
  69. createVirtualScreenshareParticipant(id, false, conference)));
  70. }
  71. }
  72. /**
  73. * Handles creating and removing virtual screenshare participants.
  74. *
  75. * @param {*} store - The redux store.
  76. * @returns {void}
  77. */
  78. function _updateScreenshareParticipants(store: IStore): void {
  79. const { dispatch, getState } = store;
  80. const state = getState();
  81. const conference = getCurrentConference(state);
  82. const tracks = state['features/base/tracks'];
  83. const { sortedRemoteVirtualScreenshareParticipants, localScreenShare } = state['features/base/participants'];
  84. const previousScreenshareSourceNames = [ ...sortedRemoteVirtualScreenshareParticipants.keys() ];
  85. let newLocalSceenshareSourceName;
  86. const currentScreenshareSourceNames = tracks.reduce((acc: string[], track) => {
  87. if (track.videoType === VIDEO_TYPE.DESKTOP && !track.jitsiTrack.isMuted()) {
  88. const sourceName: string = track.jitsiTrack.getSourceName();
  89. // Ignore orphan tracks in ssrc-rewriting mode.
  90. if (!sourceName && getSsrcRewritingFeatureFlag(state)) {
  91. return acc;
  92. }
  93. if (track.local) {
  94. newLocalSceenshareSourceName = sourceName;
  95. } else if (getParticipantById(state, getVirtualScreenshareParticipantOwnerId(sourceName))) {
  96. acc.push(sourceName);
  97. }
  98. }
  99. return acc;
  100. }, []);
  101. if (!localScreenShare && newLocalSceenshareSourceName) {
  102. dispatch(createVirtualScreenshareParticipant(newLocalSceenshareSourceName, true, conference));
  103. }
  104. if (localScreenShare && !newLocalSceenshareSourceName) {
  105. dispatch(participantLeft(localScreenShare.id, conference, {
  106. fakeParticipant: FakeParticipant.LocalScreenShare
  107. }));
  108. }
  109. if (getSsrcRewritingFeatureFlag(state)) {
  110. return;
  111. }
  112. _createOrRemoveVirtualParticipants(previousScreenshareSourceNames, currentScreenshareSourceNames, store);
  113. }
  114. /**
  115. * Handles the creation and removal of remote virtual screenshare participants when ssrc-rewriting is enabled.
  116. *
  117. * @param {Object} store - The redux store.
  118. * @returns {void}
  119. */
  120. function _updateScreenshareParticipantsBasedOnPresence(store: IStore): void {
  121. const { getState } = store;
  122. const state = getState();
  123. const { sortedRemoteVirtualScreenshareParticipants } = state['features/base/participants'];
  124. const previousScreenshareSourceNames = [ ...sortedRemoteVirtualScreenshareParticipants.keys() ];
  125. const currentScreenshareSourceNames = getRemoteScreensharesBasedOnPresence(state);
  126. _createOrRemoveVirtualParticipants(previousScreenshareSourceNames, currentScreenshareSourceNames, store);
  127. }
  128. /**
  129. * Handles notifying the next speaker in the raised hand queue.
  130. *
  131. * @param {*} store - The redux store.
  132. * @returns {void}
  133. */
  134. function _notifyNextSpeakerInRaisedHandQueue(store: IStore): void {
  135. const { dispatch } = store;
  136. batch(() => {
  137. dispatch(showNotification({
  138. titleKey: 'notify.nextToSpeak',
  139. maxLines: 2
  140. }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  141. dispatch({
  142. type: NOTIFIED_TO_SPEAK
  143. });
  144. });
  145. }