Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

middleware.ts 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import { IStore } from '../app/types';
  2. import { ENDPOINT_MESSAGE_RECEIVED, NON_PARTICIPANT_MESSAGE_RECEIVED } from '../base/conference/actionTypes';
  3. import { getCurrentConference } from '../base/conference/functions';
  4. import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
  5. import StateListenerRegistry from '../base/redux/StateListenerRegistry';
  6. import { playSound } from '../base/sounds/actions';
  7. import { INCOMING_MSG_SOUND_ID } from '../chat/constants';
  8. import { arePollsDisabled } from '../conference/functions.any';
  9. import { showNotification } from '../notifications/actions';
  10. import { NOTIFICATION_TIMEOUT_TYPE, NOTIFICATION_TYPE } from '../notifications/constants';
  11. import { RECEIVE_POLL } from './actionTypes';
  12. import { clearPolls, receiveAnswer, receivePoll } from './actions';
  13. import {
  14. COMMAND_ANSWER_POLL,
  15. COMMAND_NEW_POLL,
  16. COMMAND_OLD_POLLS
  17. } from './constants';
  18. import { IAnswer, IPoll, IPollData } from './types';
  19. /**
  20. * The maximum number of answers a poll can have.
  21. */
  22. const MAX_ANSWERS = 32;
  23. /**
  24. * Set up state change listener to perform maintenance tasks when the conference
  25. * is left or failed, e.g. Clear messages or close the chat modal if it's left
  26. * open.
  27. */
  28. StateListenerRegistry.register(
  29. state => getCurrentConference(state),
  30. (conference, { dispatch }, previousConference): void => {
  31. if (conference !== previousConference) {
  32. dispatch(clearPolls());
  33. }
  34. });
  35. const parsePollData = (pollData: Partial<IPollData>): IPoll | null => {
  36. if (typeof pollData !== 'object' || pollData === null) {
  37. return null;
  38. }
  39. const { id, senderId, question, answers } = pollData;
  40. if (typeof id !== 'string' || typeof senderId !== 'string'
  41. || typeof question !== 'string' || !(answers instanceof Array)) {
  42. return null;
  43. }
  44. return {
  45. changingVote: false,
  46. senderId,
  47. question,
  48. showResults: true,
  49. lastVote: null,
  50. answers,
  51. saved: false,
  52. editing: false
  53. };
  54. };
  55. MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
  56. const result = next(action);
  57. switch (action.type) {
  58. case ENDPOINT_MESSAGE_RECEIVED: {
  59. const { participant, data } = action;
  60. const isNewPoll = data.type === COMMAND_NEW_POLL;
  61. _handleReceivePollsMessage({
  62. ...data,
  63. senderId: isNewPoll ? participant.getId() : undefined,
  64. voterId: isNewPoll ? undefined : participant.getId()
  65. }, dispatch, getState);
  66. break;
  67. }
  68. case NON_PARTICIPANT_MESSAGE_RECEIVED: {
  69. const { id, json: data } = action;
  70. const isNewPoll = data.type === COMMAND_NEW_POLL;
  71. _handleReceivePollsMessage({
  72. ...data,
  73. senderId: isNewPoll ? id : undefined,
  74. voterId: isNewPoll ? undefined : id
  75. }, dispatch, getState);
  76. break;
  77. }
  78. case RECEIVE_POLL: {
  79. const state = getState();
  80. if (arePollsDisabled(state)) {
  81. break;
  82. }
  83. const isChatOpen: boolean = state['features/chat'].isOpen;
  84. const isPollsTabFocused: boolean = state['features/chat'].isPollsTabFocused;
  85. // Finally, we notify user they received a new poll if their pane is not opened
  86. if (action.notify && (!isChatOpen || !isPollsTabFocused)) {
  87. dispatch(playSound(INCOMING_MSG_SOUND_ID));
  88. }
  89. break;
  90. }
  91. }
  92. return result;
  93. });
  94. /**
  95. * Handles receiving of polls message command.
  96. *
  97. * @param {Object} data - The json data carried by the polls message.
  98. * @param {Function} dispatch - The dispatch function.
  99. * @param {Function} getState - The getState function.
  100. *
  101. * @returns {void}
  102. */
  103. function _handleReceivePollsMessage(data: any, dispatch: IStore['dispatch'], getState: IStore['getState']) {
  104. if (arePollsDisabled(getState())) {
  105. return;
  106. }
  107. switch (data.type) {
  108. case COMMAND_NEW_POLL: {
  109. const { pollId, answers, senderId, question } = data;
  110. const tmp = {
  111. id: pollId,
  112. answers,
  113. question,
  114. senderId
  115. };
  116. // Check integrity of the poll data.
  117. // TODO(saghul): we should move this to the server side, likely by storing the
  118. // poll data in the room metadata.
  119. if (parsePollData(tmp) === null) {
  120. return;
  121. }
  122. const poll = {
  123. changingVote: false,
  124. senderId,
  125. showResults: false,
  126. lastVote: null,
  127. question,
  128. answers: answers.map((answer: string) => {
  129. return {
  130. name: answer,
  131. voters: []
  132. };
  133. }).slice(0, MAX_ANSWERS),
  134. saved: false,
  135. editing: false
  136. };
  137. dispatch(receivePoll(pollId, poll, true));
  138. dispatch(showNotification({
  139. appearance: NOTIFICATION_TYPE.NORMAL,
  140. titleKey: 'polls.notification.title',
  141. descriptionKey: 'polls.notification.description'
  142. }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  143. break;
  144. }
  145. case COMMAND_ANSWER_POLL: {
  146. const { pollId, answers, voterId } = data;
  147. const receivedAnswer: IAnswer = {
  148. voterId,
  149. pollId,
  150. answers: answers.slice(0, MAX_ANSWERS)
  151. };
  152. dispatch(receiveAnswer(pollId, receivedAnswer));
  153. break;
  154. }
  155. case COMMAND_OLD_POLLS: {
  156. const { polls } = data;
  157. for (const pollData of polls) {
  158. const poll = parsePollData(pollData);
  159. if (poll === null) {
  160. console.warn('[features/polls] Invalid old poll data');
  161. } else {
  162. dispatch(receivePoll(pollData.id, poll, false));
  163. }
  164. }
  165. break;
  166. }
  167. }
  168. }