Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

middleware.js 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // @flow
  2. import { batch } from 'react-redux';
  3. import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
  4. import { getConferenceState } from '../base/conference';
  5. import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
  6. import { MEDIA_TYPE } from '../base/media';
  7. import {
  8. getLocalParticipant,
  9. getRemoteParticipants,
  10. isLocalParticipantModerator,
  11. isParticipantModerator,
  12. PARTICIPANT_UPDATED,
  13. raiseHand
  14. } from '../base/participants';
  15. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  16. import { playSound, registerSound, unregisterSound } from '../base/sounds';
  17. import {
  18. hideNotification,
  19. showNotification
  20. } from '../notifications';
  21. import { muteLocal } from '../video-menu/actions.any';
  22. import {
  23. DISABLE_MODERATION,
  24. ENABLE_MODERATION,
  25. LOCAL_PARTICIPANT_APPROVED,
  26. LOCAL_PARTICIPANT_MODERATION_NOTIFICATION,
  27. LOCAL_PARTICIPANT_REJECTED,
  28. PARTICIPANT_APPROVED,
  29. PARTICIPANT_REJECTED,
  30. REQUEST_DISABLE_AUDIO_MODERATION,
  31. REQUEST_DISABLE_VIDEO_MODERATION,
  32. REQUEST_ENABLE_AUDIO_MODERATION,
  33. REQUEST_ENABLE_VIDEO_MODERATION
  34. } from './actionTypes';
  35. import {
  36. disableModeration,
  37. dismissPendingParticipant,
  38. dismissPendingAudioParticipant,
  39. enableModeration,
  40. localParticipantApproved,
  41. participantApproved,
  42. participantPendingAudio,
  43. localParticipantRejected,
  44. participantRejected
  45. } from './actions';
  46. import {
  47. ASKED_TO_UNMUTE_SOUND_ID, AUDIO_MODERATION_NOTIFICATION_ID,
  48. CS_MODERATION_NOTIFICATION_ID,
  49. VIDEO_MODERATION_NOTIFICATION_ID
  50. } from './constants';
  51. import {
  52. isEnabledFromState,
  53. isParticipantApproved,
  54. isParticipantPending
  55. } from './functions';
  56. import { ASKED_TO_UNMUTE_FILE } from './sounds';
  57. declare var APP: Object;
  58. MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
  59. const { type } = action;
  60. const { conference } = getConferenceState(getState());
  61. switch (type) {
  62. case APP_WILL_MOUNT: {
  63. dispatch(registerSound(ASKED_TO_UNMUTE_SOUND_ID, ASKED_TO_UNMUTE_FILE));
  64. break;
  65. }
  66. case APP_WILL_UNMOUNT: {
  67. dispatch(unregisterSound(ASKED_TO_UNMUTE_SOUND_ID));
  68. break;
  69. }
  70. case LOCAL_PARTICIPANT_MODERATION_NOTIFICATION: {
  71. let descriptionKey;
  72. let titleKey;
  73. let uid;
  74. switch (action.mediaType) {
  75. case MEDIA_TYPE.AUDIO: {
  76. titleKey = 'notify.moderationInEffectTitle';
  77. uid = AUDIO_MODERATION_NOTIFICATION_ID;
  78. break;
  79. }
  80. case MEDIA_TYPE.VIDEO: {
  81. titleKey = 'notify.moderationInEffectVideoTitle';
  82. uid = VIDEO_MODERATION_NOTIFICATION_ID;
  83. break;
  84. }
  85. case MEDIA_TYPE.PRESENTER: {
  86. titleKey = 'notify.moderationInEffectCSTitle';
  87. uid = CS_MODERATION_NOTIFICATION_ID;
  88. break;
  89. }
  90. }
  91. dispatch(showNotification({
  92. customActionNameKey: 'notify.raiseHandAction',
  93. customActionHandler: () => batch(() => {
  94. dispatch(raiseHand(true));
  95. dispatch(hideNotification(uid));
  96. }),
  97. descriptionKey,
  98. sticky: true,
  99. titleKey,
  100. uid
  101. }));
  102. break;
  103. }
  104. case REQUEST_DISABLE_AUDIO_MODERATION: {
  105. conference.disableAVModeration(MEDIA_TYPE.AUDIO);
  106. break;
  107. }
  108. case REQUEST_DISABLE_VIDEO_MODERATION: {
  109. conference.disableAVModeration(MEDIA_TYPE.VIDEO);
  110. break;
  111. }
  112. case REQUEST_ENABLE_AUDIO_MODERATION: {
  113. conference.enableAVModeration(MEDIA_TYPE.AUDIO);
  114. break;
  115. }
  116. case REQUEST_ENABLE_VIDEO_MODERATION: {
  117. conference.enableAVModeration(MEDIA_TYPE.VIDEO);
  118. break;
  119. }
  120. case PARTICIPANT_UPDATED: {
  121. const state = getState();
  122. const audioModerationEnabled = isEnabledFromState(MEDIA_TYPE.AUDIO, state);
  123. const participant = action.participant;
  124. if (participant && audioModerationEnabled) {
  125. if (isLocalParticipantModerator(state)) {
  126. // this is handled only by moderators
  127. if (participant.raisedHand) {
  128. // if participant raises hand show notification
  129. !isParticipantApproved(participant.id, MEDIA_TYPE.AUDIO)(state)
  130. && dispatch(participantPendingAudio(participant));
  131. } else {
  132. // if participant lowers hand hide notification
  133. isParticipantPending(participant, MEDIA_TYPE.AUDIO)(state)
  134. && dispatch(dismissPendingAudioParticipant(participant));
  135. }
  136. } else if (participant.id === getLocalParticipant(state).id
  137. && /* the new role */ isParticipantModerator(participant)) {
  138. // this is the granted moderator case
  139. getRemoteParticipants(state).forEach(p => {
  140. p.raisedHand && !isParticipantApproved(p.id, MEDIA_TYPE.AUDIO)(state)
  141. && dispatch(participantPendingAudio(p));
  142. });
  143. }
  144. }
  145. break;
  146. }
  147. case ENABLE_MODERATION: {
  148. if (typeof APP !== 'undefined') {
  149. APP.API.notifyModerationChanged(action.mediaType, true);
  150. }
  151. break;
  152. }
  153. case DISABLE_MODERATION: {
  154. if (typeof APP !== 'undefined') {
  155. APP.API.notifyModerationChanged(action.mediaType, false);
  156. }
  157. break;
  158. }
  159. case LOCAL_PARTICIPANT_APPROVED: {
  160. if (typeof APP !== 'undefined') {
  161. const local = getLocalParticipant(getState());
  162. APP.API.notifyParticipantApproved(local.id, action.mediaType);
  163. }
  164. break;
  165. }
  166. case PARTICIPANT_APPROVED: {
  167. if (typeof APP !== 'undefined') {
  168. APP.API.notifyParticipantApproved(action.id, action.mediaType);
  169. }
  170. break;
  171. }
  172. case LOCAL_PARTICIPANT_REJECTED: {
  173. if (typeof APP !== 'undefined') {
  174. const local = getLocalParticipant(getState());
  175. APP.API.notifyParticipantRejected(local.id, action.mediaType);
  176. }
  177. break;
  178. }
  179. case PARTICIPANT_REJECTED: {
  180. if (typeof APP !== 'undefined') {
  181. APP.API.notifyParticipantRejected(action.id, action.mediaType);
  182. }
  183. break;
  184. }
  185. }
  186. return next(action);
  187. });
  188. /**
  189. * Registers a change handler for state['features/base/conference'].conference to
  190. * set the event listeners needed for the A/V moderation feature to operate.
  191. */
  192. StateListenerRegistry.register(
  193. state => state['features/base/conference'].conference,
  194. (conference, { dispatch }, previousConference) => {
  195. if (conference && !previousConference) {
  196. // local participant is allowed to unmute
  197. conference.on(JitsiConferenceEvents.AV_MODERATION_APPROVED, ({ mediaType }) => {
  198. dispatch(localParticipantApproved(mediaType));
  199. // Audio & video moderation are both enabled at the same time.
  200. // Avoid displaying 2 different notifications.
  201. if (mediaType === MEDIA_TYPE.AUDIO) {
  202. dispatch(showNotification({
  203. titleKey: 'notify.hostAskedUnmute',
  204. sticky: true,
  205. customActionNameKey: 'notify.unmute',
  206. customActionHandler: () => dispatch(muteLocal(false, MEDIA_TYPE.AUDIO))
  207. }));
  208. dispatch(playSound(ASKED_TO_UNMUTE_SOUND_ID));
  209. }
  210. });
  211. conference.on(JitsiConferenceEvents.AV_MODERATION_REJECTED, ({ mediaType }) => {
  212. dispatch(localParticipantRejected(mediaType));
  213. });
  214. conference.on(JitsiConferenceEvents.AV_MODERATION_CHANGED, ({ enabled, mediaType, actor }) => {
  215. enabled ? dispatch(enableModeration(mediaType, actor)) : dispatch(disableModeration(mediaType, actor));
  216. });
  217. // this is received by moderators
  218. conference.on(
  219. JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_APPROVED,
  220. ({ participant, mediaType }) => {
  221. const { _id: id } = participant;
  222. batch(() => {
  223. // store in the whitelist
  224. dispatch(participantApproved(id, mediaType));
  225. // remove from pending list
  226. dispatch(dismissPendingParticipant(id, mediaType));
  227. });
  228. });
  229. // this is received by moderators
  230. conference.on(
  231. JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_REJECTED,
  232. ({ participant, mediaType }) => {
  233. const { _id: id } = participant;
  234. dispatch(participantRejected(id, mediaType));
  235. });
  236. }
  237. });