fix(polls): Fix 'Skip' button functionality

This commit is contained in:
Vlad Piersec 2021-10-12 14:04:16 +03:00 committed by vp8x8
parent 09efaa0d0d
commit 70af0d6b78
12 changed files with 105 additions and 35 deletions

View File

@ -1,5 +1,16 @@
// @flow
/**
* The type of the action which signals that a Poll will be changed
*
* {
* type: CHANGE_VOTE,
* }
*
*/
export const CHANGE_VOTE = 'CHANGE_VOTE';
/**
* The type of the action which signals that a new Poll was received.
*

View File

@ -1,6 +1,7 @@
// @flow
import {
CHANGE_VOTE,
RESET_NB_UNREAD_POLLS,
RECEIVE_ANSWER,
RECEIVE_POLL,
@ -9,6 +10,26 @@ import {
} from './actionTypes';
import type { Answer, Poll } from './types';
/**
* Action to signal that a poll's vote will be changed.
*
* @param {string} pollId - The id of the incoming poll.
* @param {boolean} value - The value of the 'changing' state.
* @returns {{
* type: CHANGE_VOTE,
* pollId: string,
* value: boolean
* }}
*/
export const setVoteChanging = (pollId: string, value: boolean) => {
return {
type: CHANGE_VOTE,
pollId,
value
};
};
/**
* Action to signal that a new poll was received.
*

View File

@ -7,7 +7,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { sendAnalytics, createPollEvent } from '../../analytics';
import { getLocalParticipant, getParticipantById } from '../../base/participants';
import { registerVote } from '../actions';
import { registerVote, setVoteChanging } from '../actions';
import { COMMAND_ANSWER_POLL } from '../constants';
import type { Poll } from '../types';
@ -27,6 +27,7 @@ export type AbstractProps = {
poll: Poll,
setCheckbox: Function,
skipAnswer: Function,
skipChangeVote: Function,
submitAnswer: Function,
t: Function,
};
@ -90,6 +91,10 @@ const AbstractPollAnswer = (Component: AbstractComponent<AbstractProps>) => (pro
}, [ pollId ]);
const skipChangeVote = useCallback(() => {
dispatch(setVoteChanging(pollId, false));
}, [ dispatch, pollId ]);
const { t } = useTranslation();
return (<Component
@ -97,6 +102,7 @@ const AbstractPollAnswer = (Component: AbstractComponent<AbstractProps>) => (pro
poll = { poll }
setCheckbox = { setCheckbox }
skipAnswer = { skipAnswer }
skipChangeVote = { skipChangeVote }
submitAnswer = { submitAnswer }
t = { t } />);

View File

@ -6,9 +6,8 @@ import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { sendAnalytics, createPollEvent } from '../../analytics';
import { getLocalParticipant, getParticipantById } from '../../base/participants/functions';
import { retractVote } from '../actions';
import { COMMAND_ANSWER_POLL } from '../constants';
import { setVoteChanging } from '../actions';
import { getPoll } from '../functions';
/**
* The type of the React {@code Component} props of inheriting component.
@ -51,7 +50,7 @@ export type AbstractProps = {
const AbstractPollResults = (Component: AbstractComponent<AbstractProps>) => (props: InputProps) => {
const { pollId } = props;
const pollDetails = useSelector(state => state['features/polls'].polls[pollId]);
const pollDetails = useSelector(getPoll(pollId));
const [ showDetails, setShowDetails ] = useState(false);
const toggleIsDetailed = useCallback(() => {
@ -95,33 +94,23 @@ const AbstractPollResults = (Component: AbstractComponent<AbstractProps>) => (pr
}, [ pollDetails.answers, showDetails ]);
const dispatch = useDispatch();
const conference: Object = useSelector(state => state['features/base/conference'].conference);
const localId = useSelector(state => getLocalParticipant(state).id);
const localParticipant = useSelector(state => getParticipantById(state, localId));
const localName: string = localParticipant ? localParticipant.name : 'Fellow Jitster';
const changeVote = useCallback(() => {
conference.sendMessage({
type: COMMAND_ANSWER_POLL,
pollId,
voterId: localId,
voterName: localName,
answers: new Array(pollDetails.answers.length).fill(false)
});
dispatch(retractVote(pollId));
dispatch(setVoteChanging(pollId, true));
sendAnalytics(createPollEvent('vote.changed'));
}, [ pollId, localId, localName, pollDetails ]);
}, [ dispatch, pollId ]);
const { t } = useTranslation();
return (<Component
answers = { answers }
changeVote = { changeVote }
haveVoted = { pollDetails.lastVote !== null }
question = { pollDetails.question }
showDetails = { showDetails }
t = { t }
toggleIsDetailed = { toggleIsDetailed } />);
return (
<Component
answers = { answers }
changeVote = { changeVote }
haveVoted = { pollDetails.lastVote !== null }
question = { pollDetails.question }
showDetails = { showDetails }
t = { t }
toggleIsDetailed = { toggleIsDetailed } />
);
};
export default AbstractPollResults;

View File

@ -18,9 +18,11 @@ const PollAnswer = (props: AbstractProps) => {
poll,
setCheckbox,
skipAnswer,
skipChangeVote,
submitAnswer,
t
} = props;
const { changingVote } = poll;
return (
<View>
@ -44,7 +46,7 @@ const PollAnswer = (props: AbstractProps) => {
<Button
color = '#3D3D3D'
mode = { BUTTON_MODES.CONTAINED }
onPress = { skipAnswer }
onPress = { changingVote ? skipChangeVote : skipAnswer }
style = { chatStyles.pollCreateButton } >
{t('polls.answer.skip')}
</Button>

View File

@ -20,7 +20,7 @@ type Props = {
}
const PollItem = ({ pollId }: Props) => {
const showResults = useSelector(state => shouldShowResults(state, pollId));
const showResults = useSelector(shouldShowResults(pollId));
return (
<View

View File

@ -13,9 +13,11 @@ const PollAnswer = (props: AbstractProps) => {
poll,
setCheckbox,
skipAnswer,
skipChangeVote,
submitAnswer,
t
} = props;
const { changingVote } = poll;
return (
<div className = 'poll-answer'>
@ -45,7 +47,7 @@ const PollAnswer = (props: AbstractProps) => {
<button
aria-label = { t('polls.answer.skip') }
className = 'poll-button poll-button-secondary poll-button-shortest'
onClick = { skipAnswer } >
onClick = { changingVote ? skipChangeVote : skipAnswer } >
<span>{t('polls.answer.skip')}</span>
</button>
<button

View File

@ -17,7 +17,7 @@ type Props = {
}
const PollItem = React.forwardRef<Props, HTMLElement>(({ pollId }, ref) => {
const showResults = useSelector(state => shouldShowResults(state, pollId));
const showResults = useSelector(shouldShowResults(pollId));
return (
<div ref = { ref }>

View File

@ -1,14 +1,28 @@
// @flow
/**
* Should poll results be shown.
* Selector creator for determining if poll results should be displayed or not.
*
* @param {Object} state - Global state.
* @param {string} id - Id of the poll.
* @returns {boolean} Should poll results be shown.
* @returns {Function}
*/
export const shouldShowResults = (state: Object, id: string) => Boolean(state['features/polls']?.polls[id].showResults);
export function shouldShowResults(id: string) {
return function(state: Object) {
return Boolean(state['features/polls']?.polls[id].showResults);
};
}
/**
* Selector creator for polls.
*
* @param {string} pollId - Id of the poll to get.
* @returns {Function}
*/
export function getPoll(pollId: string) {
return function(state: Object) {
return state['features/polls'].polls[pollId];
};
}
/**
* Selector for calculating the number of unread poll messages.

View File

@ -3,6 +3,7 @@
import { ReducerRegistry } from '../base/redux';
import {
CHANGE_VOTE,
RECEIVE_POLL,
RECEIVE_ANSWER,
REGISTER_VOTE,
@ -21,6 +22,22 @@ const INITIAL_STATE = {
ReducerRegistry.register('features/polls', (state = INITIAL_STATE, action) => {
switch (action.type) {
case CHANGE_VOTE: {
const { pollId, value } = action;
return {
...state,
polls: {
...state.polls,
[pollId]: {
...state.polls[pollId],
changingVote: value,
showResults: !value
}
}
};
}
// Reducer triggered when a poll is received
case RECEIVE_POLL: {
const newState = {
@ -93,6 +110,7 @@ ReducerRegistry.register('features/polls', (state = INITIAL_STATE, action) => {
...state.polls,
[pollId]: {
...state.polls[pollId],
changingVote: false,
lastVote: answers,
showResults: true
}

View File

@ -44,6 +44,7 @@ const parsePollData = (pollData): Poll | null => {
}
return {
changingVote: false,
senderId,
senderName,
question,
@ -63,6 +64,7 @@ StateListenerRegistry.register(
const { question, answers, pollId, senderId, senderName } = data;
const poll = {
changingVote: false,
senderId,
senderName,
showResults: false,

View File

@ -25,6 +25,11 @@ export type Answer = {
export type Poll = {
/**
* Whether the poll vote is being edited/changed.
*/
changingVote: boolean,
/**
* ID of the sender of this poll
*/