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.

actions.js 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // @flow
  2. import throttle from 'lodash/throttle';
  3. import type { Dispatch } from 'redux';
  4. import { NOTIFICATIONS_ENABLED, getFeatureFlag } from '../base/flags';
  5. import { getParticipantCount } from '../base/participants/functions';
  6. import {
  7. CLEAR_NOTIFICATIONS,
  8. HIDE_NOTIFICATION,
  9. SET_NOTIFICATIONS_ENABLED,
  10. SHOW_NOTIFICATION
  11. } from './actionTypes';
  12. import {
  13. NOTIFICATION_ICON,
  14. NOTIFICATION_TIMEOUT_TYPE,
  15. NOTIFICATION_TIMEOUT,
  16. NOTIFICATION_TYPE,
  17. SILENT_JOIN_THRESHOLD,
  18. SILENT_LEFT_THRESHOLD
  19. } from './constants';
  20. /**
  21. * Function that returns notification timeout value based on notification timeout type.
  22. *
  23. * @param {string} type - Notification type.
  24. * @param {Object} notificationTimeouts - Config notification timeouts.
  25. * @returns {number}
  26. */
  27. function getNotificationTimeout(type: ?string, notificationTimeouts: ?Object) {
  28. if (type === NOTIFICATION_TIMEOUT_TYPE.SHORT) {
  29. return notificationTimeouts?.short ?? NOTIFICATION_TIMEOUT.SHORT;
  30. } else if (type === NOTIFICATION_TIMEOUT_TYPE.MEDIUM) {
  31. return notificationTimeouts?.medium ?? NOTIFICATION_TIMEOUT.MEDIUM;
  32. } else if (type === NOTIFICATION_TIMEOUT_TYPE.LONG) {
  33. return notificationTimeouts?.long ?? NOTIFICATION_TIMEOUT.LONG;
  34. }
  35. return NOTIFICATION_TIMEOUT.STICKY;
  36. }
  37. /**
  38. * Clears (removes) all the notifications.
  39. *
  40. * @returns {{
  41. * type: CLEAR_NOTIFICATIONS
  42. * }}
  43. */
  44. export function clearNotifications() {
  45. return {
  46. type: CLEAR_NOTIFICATIONS
  47. };
  48. }
  49. /**
  50. * Removes the notification with the passed in id.
  51. *
  52. * @param {string} uid - The unique identifier for the notification to be
  53. * removed.
  54. * @returns {{
  55. * type: HIDE_NOTIFICATION,
  56. * uid: string
  57. * }}
  58. */
  59. export function hideNotification(uid: string) {
  60. return {
  61. type: HIDE_NOTIFICATION,
  62. uid
  63. };
  64. }
  65. /**
  66. * Stops notifications from being displayed.
  67. *
  68. * @param {boolean} enabled - Whether or not notifications should display.
  69. * @returns {{
  70. * type: SET_NOTIFICATIONS_ENABLED,
  71. * enabled: boolean
  72. * }}
  73. */
  74. export function setNotificationsEnabled(enabled: boolean) {
  75. return {
  76. type: SET_NOTIFICATIONS_ENABLED,
  77. enabled
  78. };
  79. }
  80. /**
  81. * Queues an error notification for display.
  82. *
  83. * @param {Object} props - The props needed to show the notification component.
  84. * @param {string} type - Notification type.
  85. * @returns {Object}
  86. */
  87. export function showErrorNotification(props: Object, type: ?string) {
  88. return showNotification({
  89. ...props,
  90. appearance: NOTIFICATION_TYPE.ERROR
  91. }, type);
  92. }
  93. /**
  94. * Queues a notification for display.
  95. *
  96. * @param {Object} props - The props needed to show the notification component.
  97. * @param {string} type - Timeout type.
  98. * @returns {Function}
  99. */
  100. export function showNotification(props: Object = {}, type: ?string) {
  101. return function(dispatch: Function, getState: Function) {
  102. const { disabledNotifications = [], notifications, notificationTimeouts } = getState()['features/base/config'];
  103. const enabledFlag = getFeatureFlag(getState(), NOTIFICATIONS_ENABLED, true);
  104. const shouldDisplay = enabledFlag
  105. && !(disabledNotifications.includes(props.descriptionKey)
  106. || disabledNotifications.includes(props.titleKey))
  107. && (!notifications
  108. || notifications.includes(props.descriptionKey)
  109. || notifications.includes(props.titleKey));
  110. if (shouldDisplay) {
  111. return dispatch({
  112. type: SHOW_NOTIFICATION,
  113. props,
  114. timeout: getNotificationTimeout(type, notificationTimeouts),
  115. uid: props.uid || window.Date.now().toString()
  116. });
  117. }
  118. };
  119. }
  120. /**
  121. * Queues a warning notification for display.
  122. *
  123. * @param {Object} props - The props needed to show the notification component.
  124. * @param {string} type - Notification type.
  125. * @returns {Object}
  126. */
  127. export function showWarningNotification(props: Object, type: ?string) {
  128. return showNotification({
  129. ...props,
  130. appearance: NOTIFICATION_TYPE.WARNING
  131. }, type);
  132. }
  133. /**
  134. * Queues a message notification for display.
  135. *
  136. * @param {Object} props - The props needed to show the notification component.
  137. * @param {string} type - Notification type.
  138. * @returns {Object}
  139. */
  140. export function showMessageNotification(props: Object, type: ?string) {
  141. return showNotification({
  142. ...props,
  143. concatText: true,
  144. titleKey: 'notify.chatMessages',
  145. appearance: NOTIFICATION_TYPE.NORMAL,
  146. icon: NOTIFICATION_ICON.MESSAGE
  147. }, type);
  148. }
  149. /**
  150. * An array of names of participants that have joined the conference. The array
  151. * is replaced with an empty array as notifications are displayed.
  152. *
  153. * @private
  154. * @type {string[]}
  155. */
  156. let joinedParticipantsNames = [];
  157. /**
  158. * A throttled internal function that takes the internal list of participant
  159. * names, {@code joinedParticipantsNames}, and triggers the display of a
  160. * notification informing of their joining.
  161. *
  162. * @private
  163. * @type {Function}
  164. */
  165. const _throttledNotifyParticipantConnected = throttle((dispatch: Dispatch<any>, getState: Function) => {
  166. const participantCount = getParticipantCount(getState());
  167. // Skip join notifications altogether for large meetings.
  168. if (participantCount > SILENT_JOIN_THRESHOLD) {
  169. joinedParticipantsNames = [];
  170. return;
  171. }
  172. const joinedParticipantsCount = joinedParticipantsNames.length;
  173. let notificationProps;
  174. if (joinedParticipantsCount >= 3) {
  175. notificationProps = {
  176. titleArguments: {
  177. name: joinedParticipantsNames[0]
  178. },
  179. titleKey: 'notify.connectedThreePlusMembers'
  180. };
  181. } else if (joinedParticipantsCount === 2) {
  182. notificationProps = {
  183. titleArguments: {
  184. first: joinedParticipantsNames[0],
  185. second: joinedParticipantsNames[1]
  186. },
  187. titleKey: 'notify.connectedTwoMembers'
  188. };
  189. } else if (joinedParticipantsCount) {
  190. notificationProps = {
  191. titleArguments: {
  192. name: joinedParticipantsNames[0]
  193. },
  194. titleKey: 'notify.connectedOneMember'
  195. };
  196. }
  197. if (notificationProps) {
  198. dispatch(
  199. showNotification(notificationProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
  200. }
  201. joinedParticipantsNames = [];
  202. }, 2000, { leading: false });
  203. /**
  204. * An array of names of participants that have left the conference. The array
  205. * is replaced with an empty array as notifications are displayed.
  206. *
  207. * @private
  208. * @type {string[]}
  209. */
  210. let leftParticipantsNames = [];
  211. /**
  212. * A throttled internal function that takes the internal list of participant
  213. * names, {@code leftParticipantsNames}, and triggers the display of a
  214. * notification informing of their leaving.
  215. *
  216. * @private
  217. * @type {Function}
  218. */
  219. const _throttledNotifyParticipantLeft = throttle((dispatch: Dispatch<any>, getState: Function) => {
  220. const participantCount = getParticipantCount(getState());
  221. // Skip left notifications altogether for large meetings.
  222. if (participantCount > SILENT_LEFT_THRESHOLD) {
  223. leftParticipantsNames = [];
  224. return;
  225. }
  226. const leftParticipantsCount = leftParticipantsNames.length;
  227. let notificationProps;
  228. if (leftParticipantsCount >= 3) {
  229. notificationProps = {
  230. titleArguments: {
  231. name: leftParticipantsNames[0]
  232. },
  233. titleKey: 'notify.leftThreePlusMembers'
  234. };
  235. } else if (leftParticipantsCount === 2) {
  236. notificationProps = {
  237. titleArguments: {
  238. first: leftParticipantsNames[0],
  239. second: leftParticipantsNames[1]
  240. },
  241. titleKey: 'notify.leftTwoMembers'
  242. };
  243. } else if (leftParticipantsCount) {
  244. notificationProps = {
  245. titleArguments: {
  246. name: leftParticipantsNames[0]
  247. },
  248. titleKey: 'notify.leftOneMember'
  249. };
  250. }
  251. if (notificationProps) {
  252. dispatch(
  253. showNotification(notificationProps, NOTIFICATION_TIMEOUT_TYPE.SHORT));
  254. }
  255. leftParticipantsNames = [];
  256. }, 2000, { leading: false });
  257. /**
  258. * Queues the display of a notification of a participant having connected to
  259. * the meeting. The notifications are batched so that quick consecutive
  260. * connection events are shown in one notification.
  261. *
  262. * @param {string} displayName - The name of the participant that connected.
  263. * @returns {Function}
  264. */
  265. export function showParticipantJoinedNotification(displayName: string) {
  266. joinedParticipantsNames.push(displayName);
  267. return (dispatch: Dispatch<any>, getState: Function) => _throttledNotifyParticipantConnected(dispatch, getState);
  268. }
  269. /**
  270. * Queues the display of a notification of a participant having left to
  271. * the meeting. The notifications are batched so that quick consecutive
  272. * connection events are shown in one notification.
  273. *
  274. * @param {string} displayName - The name of the participant that left.
  275. * @returns {Function}
  276. */
  277. export function showParticipantLeftNotification(displayName: string) {
  278. leftParticipantsNames.push(displayName);
  279. return (dispatch: Dispatch<any>, getState: Function) => _throttledNotifyParticipantLeft(dispatch, getState);
  280. }