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.

reducer.js 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // @flow
  2. import { ReducerRegistry, set } from '../redux';
  3. import { randomHexString } from '../util';
  4. import {
  5. DOMINANT_SPEAKER_CHANGED,
  6. PARTICIPANT_ID_CHANGED,
  7. PARTICIPANT_JOINED,
  8. PARTICIPANT_LEFT,
  9. PARTICIPANT_UPDATED,
  10. PIN_PARTICIPANT
  11. } from './actionTypes';
  12. import {
  13. LOCAL_PARTICIPANT_DEFAULT_ID,
  14. LOCAL_PARTICIPANT_DEFAULT_NAME,
  15. PARTICIPANT_ROLE
  16. } from './constants';
  17. /**
  18. * Participant object.
  19. * @typedef {Object} Participant
  20. * @property {string} id - Participant ID.
  21. * @property {string} name - Participant name.
  22. * @property {string} avatar - Path to participant avatar if any.
  23. * @property {string} role - Participant role.
  24. * @property {boolean} local - If true, participant is local.
  25. * @property {boolean} pinned - If true, participant is currently a
  26. * "PINNED_ENDPOINT".
  27. * @property {boolean} dominantSpeaker - If this participant is the dominant
  28. * speaker in the (associated) conference, {@code true}; otherwise,
  29. * {@code false}.
  30. * @property {string} email - Participant email.
  31. */
  32. declare var APP: Object;
  33. /**
  34. * These properties should not be bulk assigned when updating a particular
  35. * @see Participant.
  36. * @type {string[]}
  37. */
  38. const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE
  39. = [ 'dominantSpeaker', 'id', 'local', 'pinned' ];
  40. /**
  41. * Reducer function for a single participant.
  42. *
  43. * @param {Participant|undefined} state - Participant to be modified.
  44. * @param {Object} action - Action object.
  45. * @param {string} action.type - Type of action.
  46. * @param {Participant} action.participant - Information about participant to be
  47. * added/modified.
  48. * @param {JitsiConference} action.conference - Conference instance.
  49. * @private
  50. * @returns {Participant}
  51. */
  52. function _participant(state: Object = {}, action) {
  53. switch (action.type) {
  54. case DOMINANT_SPEAKER_CHANGED:
  55. // Only one dominant speaker is allowed.
  56. return (
  57. set(state, 'dominantSpeaker', state.id === action.participant.id));
  58. case PARTICIPANT_ID_CHANGED:
  59. if (state.id === action.oldValue) {
  60. return {
  61. ...state,
  62. id: action.newValue
  63. };
  64. }
  65. break;
  66. case PARTICIPANT_JOINED: {
  67. const participant = action.participant; // eslint-disable-line no-shadow
  68. const {
  69. avatarURL,
  70. connectionStatus,
  71. dominantSpeaker,
  72. email,
  73. isBot,
  74. local,
  75. pinned,
  76. role
  77. } = participant;
  78. let { avatarID, id, name } = participant;
  79. // avatarID
  80. //
  81. // TODO Get the avatarID of the local participant from localStorage.
  82. if (!avatarID && local) {
  83. avatarID = randomHexString(32);
  84. }
  85. // id
  86. //
  87. // XXX The situation of not having an ID for a remote participant should
  88. // not happen. Maybe we should raise an error in this case or generate a
  89. // random ID.
  90. if (!id && local) {
  91. id = LOCAL_PARTICIPANT_DEFAULT_ID;
  92. }
  93. // name
  94. if (!name) {
  95. // TODO Get the display name from config and/or localized.
  96. // XXX On Web the default value is handled in conference.js by
  97. // getParticipantDisplayName.
  98. if (typeof APP === 'undefined') {
  99. name
  100. = local ? LOCAL_PARTICIPANT_DEFAULT_NAME : 'Fellow Jitster';
  101. }
  102. }
  103. return {
  104. avatarID,
  105. avatarURL,
  106. connectionStatus,
  107. dominantSpeaker: dominantSpeaker || false,
  108. email,
  109. id,
  110. isBot,
  111. local: local || false,
  112. name,
  113. pinned: pinned || false,
  114. role: role || PARTICIPANT_ROLE.NONE
  115. };
  116. }
  117. case PARTICIPANT_UPDATED: {
  118. const participant = action.participant; // eslint-disable-line no-shadow
  119. const { local } = participant;
  120. let { id } = participant;
  121. if (!id && local) {
  122. id = LOCAL_PARTICIPANT_DEFAULT_ID;
  123. }
  124. if (state.id === id) {
  125. const newState = { ...state };
  126. for (const key in participant) {
  127. if (participant.hasOwnProperty(key)
  128. && PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE.indexOf(key)
  129. === -1) {
  130. newState[key] = participant[key];
  131. }
  132. }
  133. return newState;
  134. }
  135. break;
  136. }
  137. case PIN_PARTICIPANT:
  138. // Currently, only one pinned participant is allowed.
  139. return set(state, 'pinned', state.id === action.participant.id);
  140. }
  141. return state;
  142. }
  143. /**
  144. * Listen for actions which add, remove, or update the set of participants in
  145. * the conference.
  146. *
  147. * @param {Participant[]} state - List of participants to be modified.
  148. * @param {Object} action - Action object.
  149. * @param {string} action.type - Type of action.
  150. * @param {Participant} action.participant - Information about participant to be
  151. * added/removed/modified.
  152. * @returns {Participant[]}
  153. */
  154. ReducerRegistry.register('features/base/participants', (state = [], action) => {
  155. switch (action.type) {
  156. case DOMINANT_SPEAKER_CHANGED:
  157. case PARTICIPANT_ID_CHANGED:
  158. case PARTICIPANT_UPDATED:
  159. case PIN_PARTICIPANT:
  160. return state.map(p => _participant(p, action));
  161. case PARTICIPANT_JOINED:
  162. return [ ...state, _participant(undefined, action) ];
  163. case PARTICIPANT_LEFT:
  164. return state.filter(p => p.id !== action.participant.id);
  165. default:
  166. return state;
  167. }
  168. });