"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-7319/react/features/reactions/middleware.ts" (6 Jun 2023, 9220 Bytes) of package /linux/misc/jitsi-meet-7319.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "middleware.ts": jitsi-meet_8319_vs_jitsi-meet_8615.

    1 import { batch } from 'react-redux';
    2 import { AnyAction } from 'redux';
    3 
    4 import { createReactionSoundsDisabledEvent } from '../analytics/AnalyticsEvents';
    5 import { sendAnalytics } from '../analytics/functions';
    6 import { IStore } from '../app/types';
    7 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
    8 import { CONFERENCE_JOIN_IN_PROGRESS, SET_START_REACTIONS_MUTED } from '../base/conference/actionTypes';
    9 import { setStartReactionsMuted } from '../base/conference/actions';
   10 import {
   11     getParticipantById,
   12     getParticipantCount,
   13     isLocalParticipantModerator
   14 } from '../base/participants/functions';
   15 import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
   16 import { SETTINGS_UPDATED } from '../base/settings/actionTypes';
   17 import { updateSettings } from '../base/settings/actions';
   18 import { playSound, registerSound, unregisterSound } from '../base/sounds/actions';
   19 import { getDisabledSounds } from '../base/sounds/functions.any';
   20 import { showNotification } from '../notifications/actions';
   21 import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants';
   22 
   23 import {
   24     ADD_REACTION_BUFFER,
   25     FLUSH_REACTION_BUFFER,
   26     PUSH_REACTIONS,
   27     SEND_REACTIONS,
   28     SHOW_SOUNDS_NOTIFICATION
   29 } from './actionTypes';
   30 import {
   31     addReactionsToChat,
   32     displayReactionSoundsNotification,
   33     flushReactionBuffer,
   34     pushReactions,
   35     sendReactions,
   36     setReactionQueue
   37 } from './actions';
   38 import {
   39     ENDPOINT_REACTION_NAME,
   40     IMuteCommandAttributes,
   41     MUTE_REACTIONS_COMMAND,
   42     RAISE_HAND_SOUND_ID,
   43     REACTIONS,
   44     REACTION_SOUND,
   45     SOUNDS_THRESHOLDS
   46 } from './constants';
   47 import {
   48     getReactionMessageFromBuffer,
   49     getReactionsSoundsThresholds,
   50     getReactionsWithId,
   51     sendReactionsWebhook
   52 } from './functions.any';
   53 import logger from './logger';
   54 import { RAISE_HAND_SOUND_FILE } from './sounds';
   55 
   56 /**
   57  * Middleware which intercepts Reactions actions to handle changes to the
   58  * visibility timeout of the Reactions.
   59  *
   60  * @param {IStore} store - The redux store.
   61  * @returns {Function}
   62  */
   63 MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyAction) => {
   64     const { dispatch, getState } = store;
   65 
   66     switch (action.type) {
   67     case APP_WILL_MOUNT:
   68         batch(() => {
   69             Object.keys(REACTIONS).forEach(key => {
   70                 for (let i = 0; i < SOUNDS_THRESHOLDS.length; i++) {
   71                     dispatch(registerSound(
   72                         `${REACTIONS[key].soundId}${SOUNDS_THRESHOLDS[i]}`,
   73                         REACTIONS[key].soundFiles[i]
   74                     )
   75                     );
   76                 }
   77             }
   78             );
   79             dispatch(registerSound(RAISE_HAND_SOUND_ID, RAISE_HAND_SOUND_FILE));
   80         });
   81         break;
   82 
   83     case APP_WILL_UNMOUNT:
   84         batch(() => {
   85             Object.keys(REACTIONS).forEach(key => {
   86                 for (let i = 0; i < SOUNDS_THRESHOLDS.length; i++) {
   87                     dispatch(unregisterSound(`${REACTIONS[key].soundId}${SOUNDS_THRESHOLDS[i]}`));
   88                 }
   89             });
   90             dispatch(unregisterSound(RAISE_HAND_SOUND_ID));
   91         });
   92         break;
   93 
   94     case ADD_REACTION_BUFFER: {
   95         const { timeoutID, buffer } = getState()['features/reactions'];
   96         const { reaction } = action;
   97 
   98         clearTimeout(timeoutID ?? 0);
   99         buffer.push(reaction);
  100         action.buffer = buffer;
  101         action.timeoutID = setTimeout(() => {
  102             dispatch(flushReactionBuffer());
  103         }, 500);
  104 
  105         break;
  106     }
  107     case CONFERENCE_JOIN_IN_PROGRESS: {
  108         const { conference } = action;
  109 
  110         conference.addCommandListener(
  111             MUTE_REACTIONS_COMMAND, ({ attributes }: { attributes: IMuteCommandAttributes; }, id: any) => {
  112                 _onMuteReactionsCommand(attributes, id, store);
  113             });
  114         break;
  115     }
  116     case FLUSH_REACTION_BUFFER: {
  117         const state = getState();
  118         const { buffer } = state['features/reactions'];
  119         const participantCount = getParticipantCount(state);
  120 
  121         batch(() => {
  122             if (participantCount > 1) {
  123                 dispatch(sendReactions());
  124             }
  125             dispatch(addReactionsToChat(getReactionMessageFromBuffer(buffer)));
  126             dispatch(pushReactions(buffer));
  127         });
  128 
  129         sendReactionsWebhook(state, buffer);
  130 
  131         break;
  132     }
  133 
  134     case PUSH_REACTIONS: {
  135         const state = getState();
  136         const { queue, notificationDisplayed } = state['features/reactions'];
  137         const { soundsReactions } = state['features/base/settings'];
  138         const disabledSounds = getDisabledSounds(state);
  139         const reactions = action.reactions;
  140 
  141         batch(() => {
  142             if (!notificationDisplayed && soundsReactions && !disabledSounds.includes(REACTION_SOUND)
  143                 && displayReactionSoundsNotification) {
  144                 dispatch(displayReactionSoundsNotification());
  145             }
  146             if (soundsReactions) {
  147                 const reactionSoundsThresholds = getReactionsSoundsThresholds(reactions);
  148 
  149                 reactionSoundsThresholds.forEach(reaction =>
  150                     dispatch(playSound(`${REACTIONS[reaction.reaction].soundId}${reaction.threshold}`))
  151                 );
  152             }
  153             dispatch(setReactionQueue([ ...queue, ...getReactionsWithId(reactions) ]));
  154         });
  155         break;
  156     }
  157 
  158     case SEND_REACTIONS: {
  159         const state = getState();
  160         const { buffer } = state['features/reactions'];
  161         const { conference } = state['features/base/conference'];
  162 
  163         if (conference) {
  164             conference.sendEndpointMessage('', {
  165                 name: ENDPOINT_REACTION_NAME,
  166                 reactions: buffer,
  167                 timestamp: Date.now()
  168             });
  169         }
  170         break;
  171     }
  172 
  173     // Settings changed for mute reactions in the meeting
  174     case SET_START_REACTIONS_MUTED: {
  175         const state = getState();
  176         const { conference } = state['features/base/conference'];
  177         const { muted, updateBackend } = action;
  178 
  179         if (conference && isLocalParticipantModerator(state) && updateBackend) {
  180             conference.sendCommand(MUTE_REACTIONS_COMMAND, { attributes: { startReactionsMuted: Boolean(muted) } });
  181         }
  182         break;
  183     }
  184 
  185     case SETTINGS_UPDATED: {
  186         const { soundsReactions } = getState()['features/base/settings'];
  187 
  188         if (action.settings.soundsReactions === false && soundsReactions === true) {
  189             sendAnalytics(createReactionSoundsDisabledEvent());
  190         }
  191         break;
  192     }
  193 
  194     case SHOW_SOUNDS_NOTIFICATION: {
  195         const state = getState();
  196         const isModerator = isLocalParticipantModerator(state);
  197         const { disableReactionsModeration } = state['features/base/config'];
  198 
  199         const customActions = [ 'notify.reactionSounds' ];
  200         const customFunctions: Function[] = [ () => dispatch(updateSettings({
  201             soundsReactions: false
  202         })) ];
  203 
  204         if (isModerator && !disableReactionsModeration) {
  205             customActions.push('notify.reactionSoundsForAll');
  206             customFunctions.push(() => batch(() => {
  207                 dispatch(setStartReactionsMuted(true));
  208                 dispatch(updateSettings({ soundsReactions: false }));
  209             }));
  210         }
  211 
  212         dispatch(showNotification({
  213             titleKey: 'toolbar.disableReactionSounds',
  214             customActionNameKey: customActions,
  215             customActionHandler: customFunctions
  216         }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  217         break;
  218     }
  219     }
  220 
  221     return next(action);
  222 });
  223 
  224 /**
  225  * Notifies this instance about a "Mute Reaction Sounds" command received by the Jitsi
  226  * conference.
  227  *
  228  * @param {Object} attributes - The attributes carried by the command.
  229  * @param {string} id - The identifier of the participant who issuing the
  230  * command. A notable idiosyncrasy to be mindful of here is that the command
  231  * may be issued by the local participant.
  232  * @param {Object} store - The redux store. Used to calculate and dispatch
  233  * updates.
  234  * @private
  235  * @returns {void}
  236  */
  237 function _onMuteReactionsCommand(attributes: IMuteCommandAttributes = {}, id: string, store: IStore) {
  238     const state = store.getState();
  239 
  240     // We require to know who issued the command because (1) only a
  241     // moderator is allowed to send commands and (2) a command MUST be
  242     // issued by a defined commander.
  243     if (typeof id === 'undefined') {
  244         return;
  245     }
  246 
  247     const participantSendingCommand = getParticipantById(state, id);
  248 
  249     // The Command(s) API will send us our own commands and we don't want
  250     // to act upon them.
  251     if (participantSendingCommand?.local) {
  252         return;
  253     }
  254 
  255     if (participantSendingCommand?.role !== 'moderator') {
  256         logger.warn('Received mute-reactions command not from moderator');
  257 
  258         return;
  259     }
  260 
  261     const oldState = Boolean(state['features/base/conference'].startReactionsMuted);
  262 
  263     const newState = attributes.startReactionsMuted === 'true';
  264 
  265     if (oldState !== newState) {
  266         batch(() => {
  267             store.dispatch(setStartReactionsMuted(newState));
  268             store.dispatch(updateSettings({ soundsReactions: !newState }));
  269         });
  270     }
  271 }