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.

functions.any.ts 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import { v4 as uuidv4 } from 'uuid';
  2. import { IReduxState } from '../app/types';
  3. import { REACTIONS_ENABLED } from '../base/flags/constants';
  4. import { getFeatureFlag } from '../base/flags/functions';
  5. import { getLocalParticipant } from '../base/participants/functions';
  6. import { extractFqnFromPath } from '../dynamic-branding/functions.any';
  7. import { iAmVisitor } from '../visitors/functions';
  8. import { IReactionEmojiProps, REACTIONS, ReactionThreshold, SOUNDS_THRESHOLDS } from './constants';
  9. import logger from './logger';
  10. /**
  11. * Returns the queue of reactions.
  12. *
  13. * @param {Object} state - The state of the application.
  14. * @returns {Array}
  15. */
  16. export function getReactionsQueue(state: IReduxState): Array<IReactionEmojiProps> {
  17. return state['features/reactions'].queue;
  18. }
  19. /**
  20. * Returns chat message from reactions buffer.
  21. *
  22. * @param {Array} buffer - The reactions buffer.
  23. * @returns {string}
  24. */
  25. export function getReactionMessageFromBuffer(buffer: Array<string>): string {
  26. return buffer.map<string>(reaction => REACTIONS[reaction].message).reduce((acc, val) => `${acc}${val}`);
  27. }
  28. /**
  29. * Returns reactions array with uid.
  30. *
  31. * @param {Array} buffer - The reactions buffer.
  32. * @returns {Array}
  33. */
  34. export function getReactionsWithId(buffer: Array<string>): Array<IReactionEmojiProps> {
  35. return buffer.map<IReactionEmojiProps>(reaction => {
  36. return {
  37. reaction,
  38. uid: uuidv4()
  39. };
  40. });
  41. }
  42. /**
  43. * Sends reactions to the backend.
  44. *
  45. * @param {Object} state - The redux state object.
  46. * @param {Array} reactions - Reactions array to be sent.
  47. * @returns {void}
  48. */
  49. export async function sendReactionsWebhook(state: IReduxState, reactions: Array<string>) {
  50. const { webhookProxyUrl: url } = state['features/base/config'];
  51. const { conference } = state['features/base/conference'];
  52. const { jwt } = state['features/base/jwt'];
  53. const { connection } = state['features/base/connection'];
  54. const jid = connection?.getJid();
  55. const localParticipant = getLocalParticipant(state);
  56. const headers = {
  57. ...jwt ? { 'Authorization': `Bearer ${jwt}` } : {},
  58. 'Content-Type': 'application/json'
  59. };
  60. const reqBody = {
  61. meetingFqn: extractFqnFromPath(),
  62. sessionId: conference?.sessionId,
  63. submitted: Date.now(),
  64. reactions,
  65. participantId: localParticipant?.jwtId,
  66. participantName: localParticipant?.name,
  67. participantJid: jid
  68. };
  69. if (url) {
  70. try {
  71. const res = await fetch(`${url}/reactions`, {
  72. method: 'POST',
  73. headers,
  74. body: JSON.stringify(reqBody)
  75. });
  76. if (!res.ok) {
  77. logger.error('Status error:', res.status);
  78. }
  79. } catch (err) {
  80. logger.error('Could not send request', err);
  81. }
  82. }
  83. }
  84. /**
  85. * Returns unique reactions from the reactions buffer.
  86. *
  87. * @param {Array} reactions - The reactions buffer.
  88. * @returns {Array}
  89. */
  90. function getUniqueReactions(reactions: Array<string>): Array<string> {
  91. return [ ...new Set(reactions) ];
  92. }
  93. /**
  94. * Returns frequency of given reaction in array.
  95. *
  96. * @param {Array} reactions - Array of reactions.
  97. * @param {string} reaction - Reaction to get frequency for.
  98. * @returns {number}
  99. */
  100. function getReactionFrequency(reactions: Array<string>, reaction: string): number {
  101. return reactions.filter(r => r === reaction).length;
  102. }
  103. /**
  104. * Returns the threshold number for a given frequency.
  105. *
  106. * @param {number} frequency - Frequency of reaction.
  107. * @returns {number}
  108. */
  109. function getSoundThresholdByFrequency(frequency: number): number {
  110. for (const i of SOUNDS_THRESHOLDS) {
  111. if (frequency <= i) {
  112. return i;
  113. }
  114. }
  115. return SOUNDS_THRESHOLDS[SOUNDS_THRESHOLDS.length - 1];
  116. }
  117. /**
  118. * Returns unique reactions with threshold.
  119. *
  120. * @param {Array} reactions - The reactions buffer.
  121. * @returns {Array}
  122. */
  123. export function getReactionsSoundsThresholds(reactions: Array<string>): Array<ReactionThreshold> {
  124. const unique = getUniqueReactions(reactions);
  125. return unique.map<ReactionThreshold>(reaction => {
  126. return {
  127. reaction,
  128. threshold: getSoundThresholdByFrequency(getReactionFrequency(reactions, reaction))
  129. };
  130. });
  131. }
  132. /**
  133. * Whether or not the reactions are enabled.
  134. *
  135. * @param {Object} state - The Redux state object.
  136. * @returns {boolean}
  137. */
  138. export function isReactionsEnabled(state: IReduxState): boolean {
  139. const { disableReactions } = state['features/base/config'];
  140. if (navigator.product === 'ReactNative') {
  141. return !disableReactions && getFeatureFlag(state, REACTIONS_ENABLED, true);
  142. }
  143. return !disableReactions;
  144. }
  145. /**
  146. * Returns true if the reactions buttons should be displayed anywhere on the page and false otherwise.
  147. *
  148. * @param {IReduxState} state - The redux state.
  149. * @returns {boolean}
  150. */
  151. export function shouldDisplayReactionsButtons(state: IReduxState): boolean {
  152. return isReactionsEnabled(state) && !iAmVisitor(state);
  153. }