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.ts 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. * Set up state change listener to perform maintenance tasks when the conference
  21. * is left or failed, e.g. Clear messages or close the chat modal if it's left
  22. * open.
  23. */
  24. StateListenerRegistry.register(
  25. state => getCurrentConference(state),
  26. (conference, { dispatch }, previousConference): void => {
  27. if (conference !== previousConference) {
  28. dispatch(clearPolls());
  29. }
  30. });
  31. const parsePollData = (pollData: IPollData): IPoll | null => {
  32. if (typeof pollData !== 'object' || pollData === null) {
  33. return null;
  34. }
  35. const { id, senderId, question, answers } = pollData;
  36. if (typeof id !== 'string' || typeof senderId !== 'string'
  37. || typeof question !== 'string' || !(answers instanceof Array)) {
  38. return null;
  39. }
  40. return {
  41. changingVote: false,
  42. senderId,
  43. question,
  44. showResults: true,
  45. lastVote: null,
  46. answers,
  47. saved: false,
  48. editing: false
  49. };
  50. };
  51. MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
  52. const result = next(action);
  53. switch (action.type) {
  54. case ENDPOINT_MESSAGE_RECEIVED: {
  55. const { participant, data } = action;
  56. const isNewPoll = data.type === COMMAND_NEW_POLL;
  57. _handleReceivePollsMessage({
  58. ...data,
  59. senderId: isNewPoll ? participant.getId() : undefined,
  60. voterId: isNewPoll ? undefined : participant.getId()
  61. }, dispatch, getState);
  62. break;
  63. }
  64. case NON_PARTICIPANT_MESSAGE_RECEIVED: {
  65. const { id, json: data } = action;
  66. const isNewPoll = data.type === COMMAND_NEW_POLL;
  67. _handleReceivePollsMessage({
  68. ...data,
  69. senderId: isNewPoll ? id : undefined,
  70. voterId: isNewPoll ? undefined : id
  71. }, dispatch, getState);
  72. break;
  73. }
  74. case RECEIVE_POLL: {
  75. const state = getState();
  76. if (arePollsDisabled(state)) {
  77. break;
  78. }
  79. const isChatOpen: boolean = state['features/chat'].isOpen;
  80. const isPollsTabFocused: boolean = state['features/chat'].isPollsTabFocused;
  81. // Finally, we notify user they received a new poll if their pane is not opened
  82. if (action.notify && (!isChatOpen || !isPollsTabFocused)) {
  83. dispatch(playSound(INCOMING_MSG_SOUND_ID));
  84. }
  85. break;
  86. }
  87. }
  88. return result;
  89. });
  90. /**
  91. * Handles receiving of polls message command.
  92. *
  93. * @param {Object} data - The json data carried by the polls message.
  94. * @param {Function} dispatch - The dispatch function.
  95. * @param {Function} getState - The getState function.
  96. *
  97. * @returns {void}
  98. */
  99. function _handleReceivePollsMessage(data: any, dispatch: IStore['dispatch'], getState: IStore['getState']) {
  100. if (arePollsDisabled(getState())) {
  101. return;
  102. }
  103. switch (data.type) {
  104. case COMMAND_NEW_POLL: {
  105. const { pollId, answers, senderId, question } = data;
  106. const poll = {
  107. changingVote: false,
  108. senderId,
  109. showResults: false,
  110. lastVote: null,
  111. question,
  112. answers: answers.map((answer: string) => {
  113. return {
  114. name: answer,
  115. voters: []
  116. };
  117. }),
  118. saved: false,
  119. editing: false
  120. };
  121. dispatch(receivePoll(pollId, poll, true));
  122. dispatch(showNotification({
  123. appearance: NOTIFICATION_TYPE.NORMAL,
  124. titleKey: 'polls.notification.title',
  125. descriptionKey: 'polls.notification.description'
  126. }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  127. break;
  128. }
  129. case COMMAND_ANSWER_POLL: {
  130. const { pollId, answers, voterId } = data;
  131. const receivedAnswer: IAnswer = {
  132. voterId,
  133. pollId,
  134. answers
  135. };
  136. dispatch(receiveAnswer(pollId, receivedAnswer));
  137. break;
  138. }
  139. case COMMAND_OLD_POLLS: {
  140. const { polls } = data;
  141. for (const pollData of polls) {
  142. const poll = parsePollData(pollData);
  143. if (poll === null) {
  144. console.warn('[features/polls] Invalid old poll data');
  145. } else {
  146. dispatch(receivePoll(pollData.id, poll, false));
  147. }
  148. }
  149. break;
  150. }
  151. }
  152. }