您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

middleware.js 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // @flow
  2. import { getJitsiMeetTransport } from '../../../modules/transport';
  3. import {
  4. CONFERENCE_FAILED,
  5. CONFERENCE_JOINED,
  6. DATA_CHANNEL_OPENED,
  7. KICKED_OUT
  8. } from '../base/conference';
  9. import { SET_CONFIG } from '../base/config';
  10. import { NOTIFY_CAMERA_ERROR, NOTIFY_MIC_ERROR } from '../base/devices';
  11. import { JitsiConferenceErrors } from '../base/lib-jitsi-meet';
  12. import {
  13. DOMINANT_SPEAKER_CHANGED,
  14. PARTICIPANT_KICKED,
  15. PARTICIPANT_LEFT,
  16. PARTICIPANT_JOINED,
  17. PARTICIPANT_ROLE_CHANGED,
  18. SET_LOADABLE_AVATAR_URL,
  19. getLocalParticipant,
  20. getParticipantById
  21. } from '../base/participants';
  22. import { MiddlewareRegistry } from '../base/redux';
  23. import { getBaseUrl } from '../base/util';
  24. import { appendSuffix } from '../display-name';
  25. import { SUBMIT_FEEDBACK_ERROR, SUBMIT_FEEDBACK_SUCCESS } from '../feedback';
  26. import { SET_FILMSTRIP_VISIBLE } from '../filmstrip';
  27. import './subscriber';
  28. declare var APP: Object;
  29. /**
  30. * The middleware of the feature {@code external-api}.
  31. *
  32. * @returns {Function}
  33. */
  34. MiddlewareRegistry.register(store => next => action => {
  35. // We need to do these before executing the rest of the middelware chain
  36. switch (action.type) {
  37. case SET_LOADABLE_AVATAR_URL: {
  38. const { id, loadableAvatarUrl } = action.participant;
  39. const participant = getParticipantById(
  40. store.getState(),
  41. id
  42. );
  43. const result = next(action);
  44. if (participant) {
  45. if (loadableAvatarUrl) {
  46. participant.loadableAvatarUrl !== loadableAvatarUrl && APP.API.notifyAvatarChanged(
  47. id,
  48. loadableAvatarUrl
  49. );
  50. } else {
  51. // There is no loadable explicit URL. In this case the Avatar component would
  52. // decide to render initials or the default avatar, but the external API needs
  53. // a URL when it needs to be rendered, so if there is no initials, we return the default
  54. // Avatar URL as if it was a usual avatar URL. If there are (or may be) initials
  55. // we send undefined to signal the api user that it's not an URL that needs to be rendered.
  56. //
  57. // NOTE: we may implement a special URL format later to signal that the avatar is based
  58. // on initials, that API consumers can handle as they want, e.g. initials://jm
  59. APP.API.notifyAvatarChanged(
  60. id,
  61. participant.name ? undefined : _getDefaultAvatarUrl()
  62. );
  63. }
  64. }
  65. return result;
  66. }
  67. }
  68. const result = next(action);
  69. // These should happen after the rest of the middleware chain ran
  70. switch (action.type) {
  71. case CONFERENCE_FAILED: {
  72. if (action.conference
  73. && action.error.name === JitsiConferenceErrors.PASSWORD_REQUIRED) {
  74. APP.API.notifyOnPasswordRequired();
  75. }
  76. break;
  77. }
  78. case CONFERENCE_JOINED: {
  79. const state = store.getState();
  80. const { defaultLocalDisplayName } = state['features/base/config'];
  81. const { room } = state['features/base/conference'];
  82. const { loadableAvatarUrl, name, id } = getLocalParticipant(state);
  83. APP.API.notifyConferenceJoined(
  84. room,
  85. id,
  86. {
  87. displayName: name,
  88. formattedDisplayName: appendSuffix(
  89. name,
  90. defaultLocalDisplayName
  91. ),
  92. avatarURL: loadableAvatarUrl
  93. }
  94. );
  95. break;
  96. }
  97. case DATA_CHANNEL_OPENED:
  98. APP.API.notifyDataChannelOpened();
  99. break;
  100. case DOMINANT_SPEAKER_CHANGED:
  101. APP.API.notifyDominantSpeakerChanged(action.participant.id);
  102. break;
  103. case KICKED_OUT:
  104. APP.API.notifyKickedOut(
  105. {
  106. id: getLocalParticipant(store.getState()).id,
  107. local: true
  108. },
  109. { id: action.participant ? action.participant.getId() : undefined }
  110. );
  111. break;
  112. case NOTIFY_CAMERA_ERROR:
  113. if (action.error) {
  114. APP.API.notifyOnCameraError(
  115. action.error.name, action.error.message);
  116. }
  117. break;
  118. case NOTIFY_MIC_ERROR:
  119. if (action.error) {
  120. APP.API.notifyOnMicError(action.error.name, action.error.message);
  121. }
  122. break;
  123. case PARTICIPANT_KICKED:
  124. APP.API.notifyKickedOut(
  125. {
  126. id: action.kicked,
  127. local: false
  128. },
  129. { id: action.kicker });
  130. break;
  131. case PARTICIPANT_LEFT:
  132. APP.API.notifyUserLeft(action.participant.id);
  133. break;
  134. case PARTICIPANT_JOINED: {
  135. const state = store.getState();
  136. const { defaultRemoteDisplayName } = state['features/base/config'];
  137. const { participant } = action;
  138. const { id, local, name } = participant;
  139. // The version of external api outside of middleware did not emit
  140. // the local participant being created.
  141. if (!local) {
  142. APP.API.notifyUserJoined(id, {
  143. displayName: name,
  144. formattedDisplayName: appendSuffix(
  145. name || defaultRemoteDisplayName)
  146. });
  147. }
  148. break;
  149. }
  150. case PARTICIPANT_ROLE_CHANGED:
  151. APP.API.notifyUserRoleChanged(action.participant.id, action.participant.role);
  152. break;
  153. case SET_CONFIG: {
  154. const state = store.getState();
  155. const { disableBeforeUnloadHandlers = false } = state['features/base/config'];
  156. /**
  157. * Disposing the API when the user closes the page.
  158. */
  159. window.addEventListener(disableBeforeUnloadHandlers ? 'unload' : 'beforeunload', () => {
  160. APP.API.notifyConferenceLeft(APP.conference.roomName);
  161. APP.API.dispose();
  162. getJitsiMeetTransport().dispose();
  163. });
  164. break;
  165. }
  166. case SET_FILMSTRIP_VISIBLE:
  167. APP.API.notifyFilmstripDisplayChanged(action.visible);
  168. break;
  169. case SUBMIT_FEEDBACK_ERROR:
  170. APP.API.notifyFeedbackSubmitted(action.error || 'Unknown error');
  171. break;
  172. case SUBMIT_FEEDBACK_SUCCESS:
  173. APP.API.notifyFeedbackSubmitted();
  174. break;
  175. }
  176. return result;
  177. });
  178. /**
  179. * Returns the absolute URL of the default avatar.
  180. *
  181. * @returns {string}
  182. */
  183. function _getDefaultAvatarUrl() {
  184. return new URL('images/avatar.png', getBaseUrl()).href;
  185. }