Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

middleware.js 9.1KB

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