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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /* @flow */
  2. import { getCurrentConference } from '../base/conference';
  3. import {
  4. PARTICIPANT_JOINED,
  5. PARTICIPANT_LEFT,
  6. PARTICIPANT_ROLE,
  7. PARTICIPANT_UPDATED,
  8. getParticipantById,
  9. getParticipantDisplayName,
  10. getLocalParticipant
  11. } from '../base/participants';
  12. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  13. import { PARTICIPANTS_PANE_OPEN } from '../participants-pane/actionTypes';
  14. import {
  15. CLEAR_NOTIFICATIONS,
  16. HIDE_NOTIFICATION,
  17. SHOW_NOTIFICATION
  18. } from './actionTypes';
  19. import {
  20. clearNotifications,
  21. hideNotification,
  22. showNotification,
  23. showParticipantJoinedNotification,
  24. showParticipantLeftNotification
  25. } from './actions';
  26. import {
  27. NOTIFICATION_TIMEOUT_TYPE,
  28. RAISE_HAND_NOTIFICATION_ID
  29. } from './constants';
  30. import { areThereNotifications, joinLeaveNotificationsDisabled } from './functions';
  31. /**
  32. * Map of timers.
  33. *
  34. * @type {Map}
  35. */
  36. const timers = new Map();
  37. /**
  38. * Function that creates a timeout id for specific notification.
  39. *
  40. * @param {Object} notification - Notification for which we want to create a timeout.
  41. * @param {Function} dispatch - The Redux dispatch function.
  42. * @returns {void}
  43. */
  44. const createTimeoutId = (notification, dispatch) => {
  45. const {
  46. timeout,
  47. uid
  48. } = notification;
  49. if (timeout) {
  50. const timerID = setTimeout(() => {
  51. dispatch(hideNotification(uid));
  52. }, timeout);
  53. timers.set(uid, timerID);
  54. }
  55. };
  56. /**
  57. * Returns notifications state.
  58. *
  59. * @param {Object} state - Global state.
  60. * @returns {Array<Object>} - Notifications state.
  61. */
  62. const getNotifications = state => {
  63. const _visible = areThereNotifications(state);
  64. const { notifications } = state['features/notifications'];
  65. return _visible ? notifications : [];
  66. };
  67. /**
  68. * Middleware that captures actions to display notifications.
  69. *
  70. * @param {Store} store - The redux store.
  71. * @returns {Function}
  72. */
  73. MiddlewareRegistry.register(store => next => action => {
  74. const { dispatch, getState } = store;
  75. const state = getState();
  76. switch (action.type) {
  77. case CLEAR_NOTIFICATIONS: {
  78. if (navigator.product !== 'ReactNative') {
  79. const _notifications = getNotifications(state);
  80. for (const notification of _notifications) {
  81. if (timers.has(notification.uid)) {
  82. const timeout = timers.get(notification.uid);
  83. clearTimeout(timeout);
  84. timers.delete(notification.uid);
  85. }
  86. }
  87. timers.clear();
  88. }
  89. break;
  90. }
  91. case SHOW_NOTIFICATION: {
  92. if (navigator.product !== 'ReactNative') {
  93. if (timers.has(action.uid)) {
  94. const timer = timers.get(action.uid);
  95. clearTimeout(timer);
  96. timers.delete(action.uid);
  97. }
  98. createTimeoutId(action, dispatch);
  99. }
  100. break;
  101. }
  102. case HIDE_NOTIFICATION: {
  103. if (navigator.product !== 'ReactNative') {
  104. const timer = timers.get(action.uid);
  105. clearTimeout(timer);
  106. timers.delete(action.uid);
  107. }
  108. break;
  109. }
  110. case PARTICIPANT_JOINED: {
  111. const result = next(action);
  112. const { participant: p } = action;
  113. const { conference } = state['features/base/conference'];
  114. // Do not display notifications for the virtual screenshare tiles.
  115. if (conference
  116. && !p.local
  117. && !p.isVirtualScreenshareParticipant
  118. && !joinLeaveNotificationsDisabled()
  119. && !p.isReplacing) {
  120. dispatch(showParticipantJoinedNotification(
  121. getParticipantDisplayName(state, p.id)
  122. ));
  123. }
  124. return result;
  125. }
  126. case PARTICIPANT_LEFT: {
  127. if (!joinLeaveNotificationsDisabled()) {
  128. const participant = getParticipantById(
  129. store.getState(),
  130. action.participant.id
  131. );
  132. // Do not display notifications for the virtual screenshare tiles.
  133. if (participant
  134. && !participant.local
  135. && !participant.isVirtualScreenshareParticipant
  136. && !action.participant.isReplaced) {
  137. dispatch(showParticipantLeftNotification(
  138. getParticipantDisplayName(state, participant.id)
  139. ));
  140. }
  141. }
  142. return next(action);
  143. }
  144. case PARTICIPANT_UPDATED: {
  145. const { disableModeratorIndicator } = state['features/base/config'];
  146. if (disableModeratorIndicator) {
  147. return next(action);
  148. }
  149. const { id, role } = action.participant;
  150. const localParticipant = getLocalParticipant(state);
  151. if (localParticipant?.id !== id) {
  152. return next(action);
  153. }
  154. const oldParticipant = getParticipantById(state, id);
  155. const oldRole = oldParticipant?.role;
  156. if (oldRole && oldRole !== role && role === PARTICIPANT_ROLE.MODERATOR) {
  157. store.dispatch(showNotification({
  158. titleKey: 'notify.moderator'
  159. },
  160. NOTIFICATION_TIMEOUT_TYPE.SHORT));
  161. }
  162. return next(action);
  163. }
  164. case PARTICIPANTS_PANE_OPEN: {
  165. store.dispatch(hideNotification(RAISE_HAND_NOTIFICATION_ID));
  166. break;
  167. }
  168. }
  169. return next(action);
  170. });
  171. /**
  172. * StateListenerRegistry provides a reliable way to detect the leaving of a
  173. * conference, where we need to clean up the notifications.
  174. */
  175. StateListenerRegistry.register(
  176. /* selector */ state => getCurrentConference(state),
  177. /* listener */ (conference, { dispatch }) => {
  178. if (!conference) {
  179. dispatch(clearNotifications());
  180. }
  181. }
  182. );