fix(polls) refactor storage of poll data
This commit is contained in:
parent
5692c3cb4d
commit
ed139f53ca
|
@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||
import { createPollEvent } from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { getLocalParticipant, getParticipantById } from '../../base/participants/functions';
|
||||
import { getParticipantDisplayName } from '../../base/participants/functions';
|
||||
import { useBoundSelector } from '../../base/util/hooks';
|
||||
import { registerVote, setVoteChanging } from '../actions';
|
||||
import { COMMAND_ANSWER_POLL } from '../constants';
|
||||
|
@ -48,8 +48,6 @@ const AbstractPollAnswer = (Component: ComponentType<AbstractProps>) => (props:
|
|||
|
||||
const poll: IPoll = useSelector((state: IReduxState) => state['features/polls'].polls[pollId]);
|
||||
|
||||
const { id: localId } = useSelector(getLocalParticipant) ?? { id: '' };
|
||||
|
||||
const [ checkBoxStates, setCheckBoxState ] = useState(() => {
|
||||
if (poll.lastVote !== null) {
|
||||
return [ ...poll.lastVote ];
|
||||
|
@ -57,7 +55,8 @@ const AbstractPollAnswer = (Component: ComponentType<AbstractProps>) => (props:
|
|||
|
||||
return new Array(poll.answers.length).fill(false);
|
||||
});
|
||||
const participant = useBoundSelector(getParticipantById, poll.senderId);
|
||||
|
||||
const participantName = useBoundSelector(getParticipantDisplayName, poll.senderId);
|
||||
|
||||
const setCheckbox = useCallback((index, state) => {
|
||||
const newCheckBoxStates = [ ...checkBoxStates ];
|
||||
|
@ -69,15 +68,10 @@ const AbstractPollAnswer = (Component: ComponentType<AbstractProps>) => (props:
|
|||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const localParticipant = useBoundSelector(getParticipantById, localId);
|
||||
const localName: string = localParticipant.name ? localParticipant.name : 'Fellow Jitster';
|
||||
|
||||
const submitAnswer = useCallback(() => {
|
||||
conference.sendMessage({
|
||||
type: COMMAND_ANSWER_POLL,
|
||||
pollId,
|
||||
voterId: localId,
|
||||
voterName: localName,
|
||||
answers: checkBoxStates
|
||||
});
|
||||
|
||||
|
@ -85,7 +79,7 @@ const AbstractPollAnswer = (Component: ComponentType<AbstractProps>) => (props:
|
|||
dispatch(registerVote(pollId, checkBoxStates));
|
||||
|
||||
return false;
|
||||
}, [ pollId, localId, localName, checkBoxStates, conference ]);
|
||||
}, [ pollId, checkBoxStates, conference ]);
|
||||
|
||||
const skipAnswer = useCallback(() => {
|
||||
dispatch(registerVote(pollId, null));
|
||||
|
@ -101,7 +95,7 @@ const AbstractPollAnswer = (Component: ComponentType<AbstractProps>) => (props:
|
|||
|
||||
return (<Component
|
||||
checkBoxStates = { checkBoxStates }
|
||||
creatorName = { participant ? participant.name : '' }
|
||||
creatorName = { participantName }
|
||||
poll = { poll }
|
||||
setCheckbox = { setCheckbox }
|
||||
skipAnswer = { skipAnswer }
|
||||
|
|
|
@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { createPollEvent, sendAnalytics } from '../../analytics';
|
||||
import { getParticipantDisplayName } from '../../base/participants';
|
||||
import { COMMAND_NEW_POLL } from '../constants';
|
||||
|
||||
/**
|
||||
|
@ -85,8 +84,6 @@ const AbstractPollCreate = (Component: AbstractComponent<AbstractProps>) => (pro
|
|||
});
|
||||
|
||||
const conference = useSelector(state => state['features/base/conference'].conference);
|
||||
const myId = conference.myUserId();
|
||||
const myName = useSelector(state => getParticipantDisplayName(state, myId));
|
||||
|
||||
const onSubmit = useCallback(ev => {
|
||||
if (ev) {
|
||||
|
@ -102,8 +99,6 @@ const AbstractPollCreate = (Component: AbstractComponent<AbstractProps>) => (pro
|
|||
conference.sendMessage({
|
||||
type: COMMAND_NEW_POLL,
|
||||
pollId: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36),
|
||||
senderId: myId,
|
||||
senderName: myName,
|
||||
question,
|
||||
answers: filteredAnswers
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { createPollEvent, sendAnalytics } from '../../analytics';
|
||||
import { getParticipantDisplayName } from '../../base/participants';
|
||||
import { getParticipantById } from '../../base/participants/functions';
|
||||
import { useBoundSelector } from '../../base/util/hooks';
|
||||
import { setVoteChanging } from '../actions';
|
||||
|
@ -55,6 +56,7 @@ const AbstractPollResults = (Component: AbstractComponent<AbstractProps>) => (pr
|
|||
|
||||
const pollDetails = useSelector(getPoll(pollId));
|
||||
const participant = useBoundSelector(getParticipantById, pollDetails.senderId);
|
||||
const reduxState = useSelector(state => state);
|
||||
|
||||
const [ showDetails, setShowDetails ] = useState(false);
|
||||
const toggleIsDetailed = useCallback(() => {
|
||||
|
@ -63,27 +65,29 @@ const AbstractPollResults = (Component: AbstractComponent<AbstractProps>) => (pr
|
|||
});
|
||||
|
||||
const answers: Array<AnswerInfo> = useMemo(() => {
|
||||
const voterSet = new Set();
|
||||
const allVoters = new Set();
|
||||
|
||||
// Getting every voters ID that participates to the poll
|
||||
for (const answer of pollDetails.answers) {
|
||||
for (const [ voterId ] of answer.voters) {
|
||||
voterSet.add(voterId);
|
||||
}
|
||||
// checking if the voters is an array for supporting old structure model
|
||||
const voters = answer.voters?.length ? answer.voters : Object.keys(answer.voters);
|
||||
|
||||
voters.forEach(voter => allVoters.add(voter));
|
||||
}
|
||||
|
||||
const totalVoters = voterSet.size;
|
||||
|
||||
return pollDetails.answers.map(answer => {
|
||||
const percentage = totalVoters === 0 ? 0 : Math.round(answer.voters.size / totalVoters * 100);
|
||||
const nrOfVotersPerAnswer = answer.voters ? Object.keys(answer.voters).length : 0;
|
||||
const percentage = allVoters.size > 0 ? Math.round(nrOfVotersPerAnswer / allVoters.size * 100) : 0;
|
||||
|
||||
let voters = null;
|
||||
|
||||
if (showDetails) {
|
||||
voters = [ ...answer.voters ].map(([ id, name ]) => {
|
||||
if (showDetails && answer.voters) {
|
||||
const answerVoters = answer.voters?.length ? [ ...answer.voters ] : Object.keys({ ...answer.voters });
|
||||
|
||||
voters = answerVoters.map(id => {
|
||||
return {
|
||||
id,
|
||||
name
|
||||
name: getParticipantDisplayName(reduxState, id)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -92,7 +96,7 @@ const AbstractPollResults = (Component: AbstractComponent<AbstractProps>) => (pr
|
|||
name: answer.name,
|
||||
percentage,
|
||||
voters,
|
||||
voterCount: answer.voters.size
|
||||
voterCount: nrOfVotersPerAnswer
|
||||
};
|
||||
});
|
||||
}, [ pollDetails.answers, showDetails ]);
|
||||
|
|
|
@ -37,39 +37,20 @@ const parsePollData = (pollData: IPollData): IPoll | null => {
|
|||
if (typeof pollData !== 'object' || pollData === null) {
|
||||
return null;
|
||||
}
|
||||
const { id, senderId, senderName, question, answers } = pollData;
|
||||
const { id, senderId, question, answers } = pollData;
|
||||
|
||||
if (typeof id !== 'string' || typeof senderId !== 'string' || typeof senderName !== 'string'
|
||||
if (typeof id !== 'string' || typeof senderId !== 'string'
|
||||
|| typeof question !== 'string' || !(answers instanceof Array)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const answersParsed = [];
|
||||
|
||||
for (const answer of answers) {
|
||||
const voters = new Map();
|
||||
|
||||
for (const [ voterId, voter ] of Object.entries(answer.voters)) {
|
||||
if (typeof voter !== 'string') {
|
||||
return null;
|
||||
}
|
||||
voters.set(voterId, voter);
|
||||
}
|
||||
|
||||
answersParsed.push({
|
||||
name: answer.name,
|
||||
voters
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
changingVote: false,
|
||||
senderId,
|
||||
senderName,
|
||||
question,
|
||||
showResults: true,
|
||||
lastVote: null,
|
||||
answers: answersParsed
|
||||
answers
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -81,9 +62,15 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
|||
const { conference } = action;
|
||||
|
||||
conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
(_: any, data: any) => _handleReceivePollsMessage(data, dispatch));
|
||||
(user: any, data: any) => {
|
||||
data.type === COMMAND_NEW_POLL ? data.senderId = user._id : data.voterId = user._id;
|
||||
_handleReceivePollsMessage(data, dispatch);
|
||||
});
|
||||
conference.on(JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,
|
||||
(_: any, data: any) => _handleReceivePollsMessage(data, dispatch));
|
||||
(id: any, data: any) => {
|
||||
data.type === COMMAND_NEW_POLL ? data.senderId = id : data.voterId = id;
|
||||
_handleReceivePollsMessage(data, dispatch);
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -118,19 +105,18 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
|||
function _handleReceivePollsMessage(data: any, dispatch: IStore['dispatch']) {
|
||||
switch (data.type) {
|
||||
case COMMAND_NEW_POLL: {
|
||||
const { question, answers, pollId, senderId, senderName } = data;
|
||||
const { question, answers, pollId, senderId } = data;
|
||||
|
||||
const poll = {
|
||||
changingVote: false,
|
||||
senderId,
|
||||
senderName,
|
||||
showResults: false,
|
||||
lastVote: null,
|
||||
question,
|
||||
answers: answers.map((answer: IAnswer) => {
|
||||
return {
|
||||
name: answer,
|
||||
voters: new Map()
|
||||
voters: []
|
||||
};
|
||||
})
|
||||
};
|
||||
|
@ -146,11 +132,10 @@ function _handleReceivePollsMessage(data: any, dispatch: IStore['dispatch']) {
|
|||
}
|
||||
|
||||
case COMMAND_ANSWER_POLL: {
|
||||
const { pollId, answers, voterId, voterName } = data;
|
||||
const { pollId, answers, voterId } = data;
|
||||
|
||||
const receivedAnswer: IAnswer = {
|
||||
voterId,
|
||||
voterName,
|
||||
pollId,
|
||||
answers
|
||||
};
|
||||
|
|
|
@ -83,21 +83,30 @@ ReducerRegistry.register<IPollsState>('features/polls', (state = INITIAL_STATE,
|
|||
// if the poll exists, we update it with the incoming answer
|
||||
const newAnswers = state.polls[pollId].answers
|
||||
.map(_answer => {
|
||||
// checking if the voters is an array for supporting old structure model
|
||||
const answerVoters = _answer.voters
|
||||
? _answer.voters.length
|
||||
? [ ..._answer.voters ] : Object.keys(_answer.voters) : [];
|
||||
|
||||
return {
|
||||
name: _answer.name,
|
||||
voters: new Map(_answer.voters)
|
||||
voters: answerVoters
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
for (let i = 0; i < newAnswers.length; i++) {
|
||||
// if the answer was chosen, we add the sender to the set of voters of this answer
|
||||
const voters = newAnswers[i].voters;
|
||||
// if the answer was chosen, we add the senderId to the array of voters of this answer
|
||||
const voters = newAnswers[i].voters as any;
|
||||
|
||||
const index = voters.indexOf(answer.voterId);
|
||||
|
||||
if (answer.answers[i]) {
|
||||
voters.set(answer.voterId, answer.voterName);
|
||||
|
||||
} else {
|
||||
voters.delete(answer.voterId);
|
||||
if (index === -1) {
|
||||
voters.push(answer.voterId);
|
||||
}
|
||||
} else if (index > -1) {
|
||||
voters.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@ export interface IAnswer {
|
|||
voterId: string;
|
||||
|
||||
/**
|
||||
* Name of the voter.
|
||||
* Name of the voter for this answer.
|
||||
*/
|
||||
voterName: string;
|
||||
voterName?: string;
|
||||
}
|
||||
|
||||
export interface IPoll {
|
||||
|
@ -27,7 +27,7 @@ export interface IPoll {
|
|||
* An array of answers:
|
||||
* the name of the answer name and a map of ids and names of voters voting for this option.
|
||||
*/
|
||||
answers: Array<{ name: string; voters: Map<string, string>; }>;
|
||||
answers: Array<{ name: string; voters: Array<string>; }>;
|
||||
|
||||
/**
|
||||
* Whether the poll vote is being edited/changed.
|
||||
|
@ -50,12 +50,6 @@ export interface IPoll {
|
|||
*/
|
||||
senderId: string;
|
||||
|
||||
/**
|
||||
* Name of the sender of this poll
|
||||
* Store poll sender name in case they exit the call.
|
||||
*/
|
||||
senderName: string;
|
||||
|
||||
/**
|
||||
* Whether the results should be shown instead of the answer form.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue