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.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. if (conference && !p.local && !joinLeaveNotificationsDisabled() && !p.isReplacing) {
  115. dispatch(showParticipantJoinedNotification(
  116. getParticipantDisplayName(state, p.id)
  117. ));
  118. }
  119. return result;
  120. }
  121. case PARTICIPANT_LEFT: {
  122. if (!joinLeaveNotificationsDisabled()) {
  123. const participant = getParticipantById(
  124. store.getState(),
  125. action.participant.id
  126. );
  127. if (participant && !participant.local && !action.participant.isReplaced) {
  128. dispatch(showParticipantLeftNotification(
  129. getParticipantDisplayName(state, participant.id)
  130. ));
  131. }
  132. }
  133. return next(action);
  134. }
  135. case PARTICIPANT_UPDATED: {
  136. const { disableModeratorIndicator } = state['features/base/config'];
  137. if (disableModeratorIndicator) {
  138. return next(action);
  139. }
  140. const { id, role } = action.participant;
  141. const localParticipant = getLocalParticipant(state);
  142. if (localParticipant?.id !== id) {
  143. return next(action);
  144. }
  145. const oldParticipant = getParticipantById(state, id);
  146. const oldRole = oldParticipant?.role;
  147. if (oldRole && oldRole !== role && role === PARTICIPANT_ROLE.MODERATOR) {
  148. store.dispatch(showNotification({
  149. titleKey: 'notify.moderator'
  150. },
  151. NOTIFICATION_TIMEOUT_TYPE.SHORT));
  152. }
  153. return next(action);
  154. }
  155. case PARTICIPANTS_PANE_OPEN: {
  156. store.dispatch(hideNotification(RAISE_HAND_NOTIFICATION_ID));
  157. break;
  158. }
  159. }
  160. return next(action);
  161. });
  162. /**
  163. * StateListenerRegistry provides a reliable way to detect the leaving of a
  164. * conference, where we need to clean up the notifications.
  165. */
  166. StateListenerRegistry.register(
  167. /* selector */ state => getCurrentConference(state),
  168. /* listener */ (conference, { dispatch }) => {
  169. if (!conference) {
  170. dispatch(clearNotifications());
  171. }
  172. }
  173. );