Files
jitsi-meet/react/features/polls/middleware.ts
Дамян Минков 469406d7cd feat(polls): Move polls to using a component (#16406)
* squash: Renames module.

* squash: Loads polls component.

* squash: Attach needed logic when components/hosts load.

* squash: Moves to use component.

* squash: Uses json-message format with types.

* squash: Checks for polls support.

* squash: Fixes comments and moves validate polls to backend.

* squash: Fix debian build.

* fix(polls): Fixes polls in breakout rooms.

* squash: Further simplify types.

Separate type that needs to go into ljm and those used only for the UI part.
Simplify answer/voter type to be unified across operations which simplifies and its logic.

* squash: Change voters structure to be {id, name}.

* squash: Update react/features/conference/functions.any.ts

Co-authored-by: Saúl Ibarra Corretgé <saghul@jitsi.org>

* squash: Drops roomJid from messages. Uses the connection information as breakout does.

---------

Co-authored-by: Saúl Ibarra Corretgé <saghul@jitsi.org>
2025-09-25 16:46:06 -05:00

135 lines
4.4 KiB
TypeScript

import { IStore } from '../app/types';
import { getCurrentConference } from '../base/conference/functions';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { getParticipantById, getParticipantDisplayName } from '../base/participants/functions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
import { playSound } from '../base/sounds/actions';
import { ChatTabs, INCOMING_MSG_SOUND_ID } from '../chat/constants';
import { arePollsDisabled } from '../conference/functions.any';
import { showNotification } from '../notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE, NOTIFICATION_TYPE } from '../notifications/constants';
import { RECEIVE_POLL } from './actionTypes';
import { clearPolls, receiveAnswer, receivePoll } from './actions';
import { IIncomingAnswerData } from './types';
/**
* The maximum number of answers a poll can have.
*/
const MAX_ANSWERS = 32;
/**
* Set up state change listener to perform maintenance tasks when the conference
* is left or failed, e.g. Clear messages or close the chat modal if it's left
* open.
* When joining new conference set up the listeners for polls.
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, { dispatch, getState }, previousConference): void => {
if (conference !== previousConference) {
dispatch(clearPolls());
if (conference && !previousConference) {
conference.on(JitsiConferenceEvents.POLL_RECEIVED, (data: any) => {
_handleReceivedPollsData(data, dispatch, getState);
});
conference.on(JitsiConferenceEvents.POLL_ANSWER_RECEIVED, (data: any) => {
_handleReceivedPollsAnswer(data, dispatch, getState);
});
}
}
});
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);
switch (action.type) {
case RECEIVE_POLL: {
const state = getState();
if (arePollsDisabled(state)) {
break;
}
const isChatOpen: boolean = state['features/chat'].isOpen;
const isPollsTabFocused: boolean = state['features/chat'].focusedTab === ChatTabs.POLLS;
// Finally, we notify user they received a new poll if their pane is not opened
if (action.notify && (!isChatOpen || !isPollsTabFocused)) {
dispatch(playSound(INCOMING_MSG_SOUND_ID));
}
break;
}
}
return result;
});
/**
* Handles receiving of new or history polls to load.
*
* @param {Object} data - The json data carried by the polls message.
* @param {Function} dispatch - The dispatch function.
* @param {Function} getState - The getState function.
*
* @returns {void}
*/
function _handleReceivedPollsData(data: any, dispatch: IStore['dispatch'], getState: IStore['getState']) {
if (arePollsDisabled(getState())) {
return;
}
const { pollId, answers, senderId, question, history } = data;
const poll = {
changingVote: false,
senderId,
showResults: false,
lastVote: null,
question,
answers: answers.slice(0, MAX_ANSWERS),
saved: false,
editing: false,
pollId
};
dispatch(receivePoll(poll, !history));
if (!history) {
dispatch(showNotification({
appearance: NOTIFICATION_TYPE.NORMAL,
titleKey: 'polls.notification.title',
descriptionKey: 'polls.notification.description'
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
}
}
/**
* Handles receiving of pools answers.
*
* @param {Object} data - The json data carried by the polls message.
* @param {Function} dispatch - The dispatch function.
* @param {Function} getState - The getState function.
*
* @returns {void}
*/
function _handleReceivedPollsAnswer(data: any, dispatch: IStore['dispatch'], getState: IStore['getState']) {
if (arePollsDisabled(getState())) {
return;
}
const { pollId, answers, senderId, senderName } = data;
const receivedAnswer: IIncomingAnswerData = {
answers: answers.slice(0, MAX_ANSWERS).map(Boolean),
pollId,
senderId,
voterName: getParticipantById(getState(), senderId)
? getParticipantDisplayName(getState(), senderId) : senderName
};
dispatch(receiveAnswer(receivedAnswer));
}