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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // @flow
  2. import { CONFERENCE_LEFT, getCurrentConference } from '../base/conference';
  3. import {
  4. PARTICIPANT_LEFT,
  5. getLocalParticipant,
  6. participantJoined,
  7. participantLeft,
  8. pinParticipant
  9. } from '../base/participants';
  10. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  11. import { TOGGLE_SHARED_VIDEO, SET_SHARED_VIDEO_STATUS } from './actionTypes';
  12. import { setSharedVideoStatus, showEnterVideoLinkPrompt } from './actions';
  13. const SHARED_VIDEO = 'shared-video';
  14. /**
  15. * Middleware that captures actions related to YouTube video sharing and updates
  16. * components not hooked into redux.
  17. *
  18. * @param {Store} store - The redux store.
  19. * @returns {Function}
  20. */
  21. MiddlewareRegistry.register(store => next => action => {
  22. const { dispatch, getState } = store;
  23. const state = getState();
  24. const conference = getCurrentConference(state);
  25. const localParticipantId = getLocalParticipant(state)?.id;
  26. const { videoId, status, ownerId, time } = action;
  27. switch (action.type) {
  28. case TOGGLE_SHARED_VIDEO:
  29. _toggleSharedVideo(store, next, action);
  30. break;
  31. case CONFERENCE_LEFT:
  32. dispatch(setSharedVideoStatus('', 'stop', 0, ''));
  33. break;
  34. case PARTICIPANT_LEFT:
  35. if (action.participant.id === action.ownerId) {
  36. dispatch(setSharedVideoStatus('', 'stop', 0, ''));
  37. }
  38. break;
  39. case SET_SHARED_VIDEO_STATUS:
  40. if (localParticipantId === ownerId) {
  41. sendShareVideoCommand(videoId, status, conference, localParticipantId, time);
  42. }
  43. break;
  44. }
  45. return next(action);
  46. });
  47. /**
  48. * Set up state change listener to perform maintenance tasks when the conference
  49. * is left or failed, e.g. clear messages or close the chat modal if it's left
  50. * open.
  51. */
  52. StateListenerRegistry.register(
  53. state => getCurrentConference(state),
  54. (conference, store, previousConference) => {
  55. if (conference && conference !== previousConference) {
  56. conference.addCommandListener(SHARED_VIDEO,
  57. ({ value, attributes }) => {
  58. const { dispatch, getState } = store;
  59. const { from } = attributes;
  60. const localParticipantId = getLocalParticipant(getState()).id;
  61. const status = attributes.state;
  62. if ([ 'playing', 'pause', 'start' ].includes(status)) {
  63. handleSharingVideoStatus(store, value, attributes, conference);
  64. } else if (status === 'stop') {
  65. dispatch(participantLeft(value, conference));
  66. if (localParticipantId !== from) {
  67. dispatch(setSharedVideoStatus(value, 'stop', 0, from));
  68. }
  69. }
  70. }
  71. );
  72. }
  73. });
  74. /**
  75. * Handles the playing, pause and start statuses for the shared video.
  76. * Dispatches participantJoined event and, if necessary, pins it.
  77. * Sets the SharedVideoStatus if the event was triggered by the local user.
  78. *
  79. * @param {Store} store - The redux store.
  80. * @param {string} videoId - The YoutubeId of the video to the shared.
  81. * @param {Object} attributes - The attributes received from the share video command.
  82. * @param {JitsiConference} conference - The current conference.
  83. * @returns {void}
  84. */
  85. function handleSharingVideoStatus(store, videoId, { state, time, from }, conference) {
  86. const { dispatch, getState } = store;
  87. const localParticipantId = getLocalParticipant(getState()).id;
  88. const oldStatus = getState()['features/youtube-player']?.status;
  89. if (state === 'start' || ![ 'playing', 'pause', 'start' ].includes(oldStatus)) {
  90. dispatch(participantJoined({
  91. conference,
  92. id: videoId,
  93. isFakeParticipant: true,
  94. avatarURL: `https://img.youtube.com/vi/${videoId}/0.jpg`,
  95. name: 'YouTube'
  96. }));
  97. dispatch(pinParticipant(videoId));
  98. }
  99. if (localParticipantId !== from) {
  100. dispatch(setSharedVideoStatus(videoId, state, time, from));
  101. }
  102. }
  103. /**
  104. * Dispatches shared video status.
  105. *
  106. * @param {Store} store - The redux store.
  107. * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
  108. * specified {@code action} in the specified {@code store}.
  109. * @param {Action} action - The redux action which is
  110. * being dispatched in the specified {@code store}.
  111. * @returns {Function}
  112. */
  113. function _toggleSharedVideo(store, next, action) {
  114. const { dispatch, getState } = store;
  115. const state = getState();
  116. const { videoId, ownerId, status } = state['features/youtube-player'];
  117. const localParticipant = getLocalParticipant(state);
  118. if (status === 'playing' || status === 'start' || status === 'pause') {
  119. if (ownerId === localParticipant.id) {
  120. dispatch(setSharedVideoStatus(videoId, 'stop', 0, localParticipant.id));
  121. }
  122. } else {
  123. dispatch(showEnterVideoLinkPrompt(id => _onVideoLinkEntered(store, id)));
  124. }
  125. return next(action);
  126. }
  127. /**
  128. * Sends SHARED_VIDEO start command.
  129. *
  130. * @param {Store} store - The redux store.
  131. * @param {string} id - The youtube id of the video to be shared.
  132. * @returns {void}
  133. */
  134. function _onVideoLinkEntered(store, id) {
  135. const { dispatch, getState } = store;
  136. const conference = getCurrentConference(getState());
  137. if (conference) {
  138. const localParticipant = getLocalParticipant(getState());
  139. dispatch(setSharedVideoStatus(id, 'start', 0, localParticipant.id));
  140. }
  141. }
  142. /* eslint-disable max-params */
  143. /**
  144. * Sends SHARED_VIDEO command.
  145. *
  146. * @param {string} id - The youtube id of the video.
  147. * @param {string} status - The status of the shared video.
  148. * @param {JitsiConference} conference - The current conference.
  149. * @param {string} localParticipantId - The id of the local participant.
  150. * @param {string} time - The seek position of the video.
  151. * @returns {void}
  152. */
  153. function sendShareVideoCommand(id, status, conference, localParticipantId, time) {
  154. conference.sendCommandOnce(SHARED_VIDEO, {
  155. value: id,
  156. attributes: {
  157. from: localParticipantId,
  158. state: status,
  159. time
  160. }
  161. });
  162. }