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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // @flow
  2. import { CONFERENCE_FAILED, CONFERENCE_JOINED } from '../base/conference';
  3. import { JitsiConferenceErrors, JitsiConferenceEvents } from '../base/lib-jitsi-meet';
  4. import { getFirstLoadableAvatarUrl, getParticipantDisplayName } from '../base/participants';
  5. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  6. import { isTestModeEnabled } from '../base/testing';
  7. import { NOTIFICATION_TYPE, showNotification } from '../notifications';
  8. import { isPrejoinPageEnabled } from '../prejoin/functions';
  9. import { KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED } from './actionTypes';
  10. import {
  11. hideLobbyScreen,
  12. knockingParticipantLeft,
  13. openLobbyScreen,
  14. participantIsKnockingOrUpdated,
  15. setLobbyModeEnabled,
  16. startKnocking,
  17. setPasswordJoinFailed
  18. } from './actions';
  19. MiddlewareRegistry.register(store => next => action => {
  20. switch (action.type) {
  21. case CONFERENCE_FAILED:
  22. return _conferenceFailed(store, next, action);
  23. case CONFERENCE_JOINED:
  24. return _conferenceJoined(store, next, action);
  25. case KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED: {
  26. // We need the full update result to be in the store already
  27. const result = next(action);
  28. _findLoadableAvatarForKnockingParticipant(store, action.participant);
  29. return result;
  30. }
  31. }
  32. return next(action);
  33. });
  34. /**
  35. * Registers a change handler for state['features/base/conference'].conference to
  36. * set the event listeners needed for the lobby feature to operate.
  37. */
  38. StateListenerRegistry.register(
  39. state => state['features/base/conference'].conference,
  40. (conference, { dispatch, getState }, previousConference) => {
  41. if (conference && !previousConference) {
  42. conference.on(JitsiConferenceEvents.MEMBERS_ONLY_CHANGED, enabled => {
  43. dispatch(setLobbyModeEnabled(enabled));
  44. });
  45. conference.on(JitsiConferenceEvents.LOBBY_USER_JOINED, (id, name) => {
  46. dispatch(participantIsKnockingOrUpdated({
  47. id,
  48. name
  49. }));
  50. });
  51. conference.on(JitsiConferenceEvents.LOBBY_USER_UPDATED, (id, participant) => {
  52. dispatch(participantIsKnockingOrUpdated({
  53. ...participant,
  54. id
  55. }));
  56. });
  57. conference.on(JitsiConferenceEvents.LOBBY_USER_LEFT, id => {
  58. dispatch(knockingParticipantLeft(id));
  59. });
  60. conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, (origin, sender) =>
  61. _maybeSendLobbyNotification(origin, sender, {
  62. dispatch,
  63. getState
  64. })
  65. );
  66. }
  67. });
  68. /**
  69. * Function to handle the conference failed event and navigate the user to the lobby screen
  70. * based on the failure reason.
  71. *
  72. * @param {Object} store - The Redux store.
  73. * @param {Function} next - The Redux next function.
  74. * @param {Object} action - The Redux action.
  75. * @returns {Object}
  76. */
  77. function _conferenceFailed({ dispatch, getState }, next, action) {
  78. const { error } = action;
  79. const state = getState();
  80. const nonFirstFailure = Boolean(state['features/base/conference'].membersOnly);
  81. if (error.name === JitsiConferenceErrors.MEMBERS_ONLY_ERROR) {
  82. if (typeof error.recoverable === 'undefined') {
  83. error.recoverable = true;
  84. }
  85. const result = next(action);
  86. dispatch(openLobbyScreen());
  87. if (isPrejoinPageEnabled(state) && !state['features/lobby'].knocking) {
  88. // prejoin is enabled, so we knock automatically
  89. dispatch(startKnocking());
  90. }
  91. dispatch(setPasswordJoinFailed(nonFirstFailure));
  92. return result;
  93. }
  94. dispatch(hideLobbyScreen());
  95. if (error.name === JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED) {
  96. dispatch(showNotification({
  97. appearance: NOTIFICATION_TYPE.ERROR,
  98. hideErrorSupportLink: true,
  99. titleKey: 'lobby.joinRejectedMessage'
  100. }));
  101. }
  102. return next(action);
  103. }
  104. /**
  105. * Handles cleanup of lobby state when a conference is joined.
  106. *
  107. * @param {Object} store - The Redux store.
  108. * @param {Function} next - The Redux next function.
  109. * @param {Object} action - The Redux action.
  110. * @returns {Object}
  111. */
  112. function _conferenceJoined({ dispatch }, next, action) {
  113. dispatch(hideLobbyScreen());
  114. return next(action);
  115. }
  116. /**
  117. * Finds the loadable avatar URL and updates the participant accordingly.
  118. *
  119. * @param {Object} store - The Redux store.
  120. * @param {Object} participant - The knocking participant.
  121. * @returns {void}
  122. */
  123. function _findLoadableAvatarForKnockingParticipant(store, { id }) {
  124. const { dispatch, getState } = store;
  125. const updatedParticipant = getState()['features/lobby'].knockingParticipants.find(p => p.id === id);
  126. const { disableThirdPartyRequests } = getState()['features/base/config'];
  127. if (!disableThirdPartyRequests && updatedParticipant && !updatedParticipant.loadableAvatarUrl) {
  128. getFirstLoadableAvatarUrl(updatedParticipant, store).then(loadableAvatarUrl => {
  129. if (loadableAvatarUrl) {
  130. dispatch(participantIsKnockingOrUpdated({
  131. loadableAvatarUrl,
  132. id
  133. }));
  134. }
  135. });
  136. }
  137. }
  138. /**
  139. * Check the endpoint message that arrived through the conference and
  140. * sends a lobby notification, if the message belongs to the feature.
  141. *
  142. * @param {Object} origin - The origin (initiator) of the message.
  143. * @param {Object} message - The actual message.
  144. * @param {Object} store - The Redux store.
  145. * @returns {void}
  146. */
  147. function _maybeSendLobbyNotification(origin, message, { dispatch, getState }) {
  148. if (!origin?._id || message?.type !== 'lobby-notify') {
  149. return;
  150. }
  151. const notificationProps: any = {
  152. descriptionArguments: {
  153. originParticipantName: getParticipantDisplayName(getState, origin._id),
  154. targetParticipantName: message.name
  155. },
  156. titleKey: 'lobby.notificationTitle'
  157. };
  158. switch (message.event) {
  159. case 'LOBBY-ENABLED':
  160. notificationProps.descriptionKey = `lobby.notificationLobby${message.value ? 'En' : 'Dis'}abled`;
  161. break;
  162. case 'LOBBY-ACCESS-GRANTED':
  163. notificationProps.descriptionKey = 'lobby.notificationLobbyAccessGranted';
  164. break;
  165. case 'LOBBY-ACCESS-DENIED':
  166. notificationProps.descriptionKey = 'lobby.notificationLobbyAccessDenied';
  167. break;
  168. }
  169. dispatch(showNotification(notificationProps, isTestModeEnabled(getState()) ? undefined : 5000));
  170. }