"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 }