Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import { ILocalParticipant, IParticipant } from '../base/participants/types';
  2. import ReducerRegistry from '../base/redux/ReducerRegistry';
  3. import {
  4. ADD_MESSAGE,
  5. ADD_MESSAGE_REACTION,
  6. CLEAR_MESSAGES,
  7. CLOSE_CHAT,
  8. EDIT_MESSAGE,
  9. OPEN_CHAT,
  10. REMOVE_LOBBY_CHAT_PARTICIPANT,
  11. SET_IS_POLL_TAB_FOCUSED,
  12. SET_LOBBY_CHAT_ACTIVE_STATE,
  13. SET_LOBBY_CHAT_RECIPIENT,
  14. SET_PRIVATE_MESSAGE_RECIPIENT
  15. } from './actionTypes';
  16. import { IMessage } from './types';
  17. const DEFAULT_STATE = {
  18. isOpen: false,
  19. isPollsTabFocused: false,
  20. lastReadMessage: undefined,
  21. messages: [],
  22. reactions: {},
  23. nbUnreadMessages: 0,
  24. privateMessageRecipient: undefined,
  25. lobbyMessageRecipient: undefined,
  26. isLobbyChatActive: false
  27. };
  28. export interface IChatState {
  29. isLobbyChatActive: boolean;
  30. isOpen: boolean;
  31. isPollsTabFocused: boolean;
  32. lastReadMessage?: IMessage;
  33. lobbyMessageRecipient?: {
  34. id: string;
  35. name: string;
  36. } | ILocalParticipant;
  37. messages: IMessage[];
  38. nbUnreadMessages: number;
  39. privateMessageRecipient?: IParticipant;
  40. }
  41. ReducerRegistry.register<IChatState>('features/chat', (state = DEFAULT_STATE, action): IChatState => {
  42. switch (action.type) {
  43. case ADD_MESSAGE: {
  44. const newMessage: IMessage = {
  45. displayName: action.displayName,
  46. error: action.error,
  47. participantId: action.participantId,
  48. isReaction: action.isReaction,
  49. messageId: action.messageId,
  50. messageType: action.messageType,
  51. message: action.message,
  52. reactions: action.reactions,
  53. privateMessage: action.privateMessage,
  54. lobbyChat: action.lobbyChat,
  55. recipient: action.recipient,
  56. timestamp: action.timestamp
  57. };
  58. // React native, unlike web, needs a reverse sorted message list.
  59. const messages = navigator.product === 'ReactNative'
  60. ? [
  61. newMessage,
  62. ...state.messages
  63. ]
  64. : [
  65. ...state.messages,
  66. newMessage
  67. ];
  68. return {
  69. ...state,
  70. lastReadMessage:
  71. action.hasRead ? newMessage : state.lastReadMessage,
  72. nbUnreadMessages: state.isPollsTabFocused ? state.nbUnreadMessages + 1 : state.nbUnreadMessages,
  73. messages
  74. };
  75. }
  76. case ADD_MESSAGE_REACTION: {
  77. const { participantId, reactionList, messageId } = action;
  78. const messages = state.messages.map(message => {
  79. if (messageId === message.messageId) {
  80. const newReactions = new Map(message.reactions);
  81. reactionList.forEach((reaction: string) => {
  82. let participants = newReactions.get(reaction);
  83. if (!participants) {
  84. participants = new Set();
  85. newReactions.set(reaction, participants);
  86. }
  87. participants.add(participantId);
  88. });
  89. return {
  90. ...message,
  91. reactions: newReactions
  92. };
  93. }
  94. return message;
  95. });
  96. return {
  97. ...state,
  98. messages
  99. };
  100. }
  101. case CLEAR_MESSAGES:
  102. return {
  103. ...state,
  104. lastReadMessage: undefined,
  105. messages: []
  106. };
  107. case EDIT_MESSAGE: {
  108. let found = false;
  109. const newMessage = action.message;
  110. const messages = state.messages.map(m => {
  111. if (m.messageId === newMessage.messageId) {
  112. found = true;
  113. return newMessage;
  114. }
  115. return m;
  116. });
  117. // no change
  118. if (!found) {
  119. return state;
  120. }
  121. return {
  122. ...state,
  123. messages
  124. };
  125. }
  126. case SET_PRIVATE_MESSAGE_RECIPIENT:
  127. return {
  128. ...state,
  129. privateMessageRecipient: action.participant
  130. };
  131. case OPEN_CHAT:
  132. return {
  133. ...state,
  134. isOpen: true,
  135. privateMessageRecipient: action.participant
  136. };
  137. case CLOSE_CHAT:
  138. return {
  139. ...state,
  140. isOpen: false,
  141. lastReadMessage: state.messages[
  142. navigator.product === 'ReactNative' ? 0 : state.messages.length - 1],
  143. privateMessageRecipient: action.participant,
  144. isLobbyChatActive: false
  145. };
  146. case SET_IS_POLL_TAB_FOCUSED: {
  147. return {
  148. ...state,
  149. isPollsTabFocused: action.isPollsTabFocused,
  150. nbUnreadMessages: 0
  151. }; }
  152. case SET_LOBBY_CHAT_RECIPIENT:
  153. return {
  154. ...state,
  155. isLobbyChatActive: true,
  156. lobbyMessageRecipient: action.participant,
  157. privateMessageRecipient: undefined,
  158. isOpen: action.open
  159. };
  160. case SET_LOBBY_CHAT_ACTIVE_STATE:
  161. return {
  162. ...state,
  163. isLobbyChatActive: action.payload,
  164. isOpen: action.payload || state.isOpen,
  165. privateMessageRecipient: undefined
  166. };
  167. case REMOVE_LOBBY_CHAT_PARTICIPANT:
  168. return {
  169. ...state,
  170. messages: state.messages.filter(m => {
  171. if (action.removeLobbyChatMessages) {
  172. return !m.lobbyChat;
  173. }
  174. return true;
  175. }),
  176. isOpen: state.isOpen && state.isLobbyChatActive ? false : state.isOpen,
  177. isLobbyChatActive: false,
  178. lobbyMessageRecipient: undefined
  179. };
  180. }
  181. return state;
  182. });