Jitsi Meet - Secure, Simple and Scalable Video Conferences that you use as a standalone app or embed in your web application.
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.
 
 
 
 
 
 
jitsi-meet/react/features/polls/middleware.ts

181 lines
5.2 KiB

import { IStore } from '../app/types';
import { ENDPOINT_MESSAGE_RECEIVED, NON_PARTICIPANT_MESSAGE_RECEIVED } from '../base/conference/actionTypes';
import { getCurrentConference } from '../base/conference/functions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
import { playSound } from '../base/sounds/actions';
import { 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 {
COMMAND_ANSWER_POLL,
COMMAND_NEW_POLL,
COMMAND_OLD_POLLS
} from './constants';
import { IAnswer, IPoll, IPollData } from './types';
/**
* 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.
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, { dispatch }, previousConference): void => {
if (conference !== previousConference) {
dispatch(clearPolls());
}
});
const parsePollData = (pollData: IPollData): IPoll | null => {
if (typeof pollData !== 'object' || pollData === null) {
return null;
}
const { id, senderId, question, answers } = pollData;
if (typeof id !== 'string' || typeof senderId !== 'string'
|| typeof question !== 'string' || !(answers instanceof Array)) {
return null;
}
return {
changingVote: false,
senderId,
question,
showResults: true,
lastVote: null,
answers,
saved: false,
editing: false
};
};
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);
switch (action.type) {
case ENDPOINT_MESSAGE_RECEIVED: {
const { participant, data } = action;
const isNewPoll = data.type === COMMAND_NEW_POLL;
_handleReceivePollsMessage({
...data,
senderId: isNewPoll ? participant.getId() : undefined,
voterId: isNewPoll ? undefined : participant.getId()
}, dispatch, getState);
break;
}
case NON_PARTICIPANT_MESSAGE_RECEIVED: {
const { id, json: data } = action;
const isNewPoll = data.type === COMMAND_NEW_POLL;
_handleReceivePollsMessage({
...data,
senderId: isNewPoll ? id : undefined,
voterId: isNewPoll ? undefined : id
}, dispatch, getState);
break;
}
case RECEIVE_POLL: {
const state = getState();
if (arePollsDisabled(state)) {
break;
}
const isChatOpen: boolean = state['features/chat'].isOpen;
const isPollsTabFocused: boolean = state['features/chat'].isPollsTabFocused;
// 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 polls message command.
*
* @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 _handleReceivePollsMessage(data: any, dispatch: IStore['dispatch'], getState: IStore['getState']) {
if (arePollsDisabled(getState())) {
return;
}
switch (data.type) {
case COMMAND_NEW_POLL: {
const { pollId, answers, senderId, question } = data;
const poll = {
changingVote: false,
senderId,
showResults: false,
lastVote: null,
question,
answers: answers.map((answer: string) => {
return {
name: answer,
voters: []
};
}),
saved: false,
editing: false
};
dispatch(receivePoll(pollId, poll, true));
dispatch(showNotification({
appearance: NOTIFICATION_TYPE.NORMAL,
titleKey: 'polls.notification.title',
descriptionKey: 'polls.notification.description'
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
break;
}
case COMMAND_ANSWER_POLL: {
const { pollId, answers, voterId } = data;
const receivedAnswer: IAnswer = {
voterId,
pollId,
answers
};
dispatch(receiveAnswer(pollId, receivedAnswer));
break;
}
case COMMAND_OLD_POLLS: {
const { polls } = data;
for (const pollData of polls) {
const poll = parsePollData(pollData);
if (poll === null) {
console.warn('[features/polls] Invalid old poll data');
} else {
dispatch(receivePoll(pollData.id, poll, false));
}
}
break;
}
}
}