feat(polls/web/native): refactoring (#14778)

* feat(polls/web/native): refactoring
This commit is contained in:
Calinteodor
2024-05-27 12:14:17 +03:00
committed by GitHub
parent d49a419284
commit c31ef2ebc7
10 changed files with 68 additions and 36 deletions

View File

@@ -92,7 +92,7 @@ const AbstractPollAnswer = (Component: ComponentType<AbstractProps>) => (props:
type: COMMAND_NEW_POLL,
pollId,
question,
answers
answers: answers.map(answer => answer.name)
});
dispatch(editPoll(pollId, false));

View File

@@ -10,7 +10,7 @@ import { IReduxState } from '../../app/types';
import { getLocalParticipant } from '../../base/participants/functions';
import { savePoll } from '../actions';
import { hasIdenticalAnswers } from '../functions';
import { IPoll } from '../types';
import { IAnswerData, IPoll } from '../types';
/**
* The type of the React {@code Component} props of inheriting component.
@@ -25,14 +25,14 @@ type InputProps = {
**/
export type AbstractProps = InputProps & {
addAnswer: (index?: number) => void;
answers: Array<string>;
answers: Array<IAnswerData>;
editingPoll: IPoll | undefined;
editingPollId: string | undefined;
isSubmitDisabled: boolean;
onSubmit: (event?: FormEvent<HTMLFormElement>) => void;
question: string;
removeAnswer: (index: number) => void;
setAnswer: (index: number, value: string) => void;
setAnswer: (index: number, value: IAnswerData) => void;
setQuestion: (question: string) => void;
t: Function;
};
@@ -67,7 +67,17 @@ const AbstractPollCreate = (Component: ComponentType<AbstractProps>) => (props:
}, [ pollState ]);
const answerResults = useMemo(() => {
return editingPoll ? editingPoll[1].answers as Array<string> : [ '', '' ];
return editingPoll
? editingPoll[1].answers
: [
{
name: '',
voters: []
},
{
name: '',
voters: []
} ];
}, [ editingPoll ]);
const questionResult = useMemo(() => {
@@ -78,7 +88,7 @@ const AbstractPollCreate = (Component: ComponentType<AbstractProps>) => (props:
const [ answers, setAnswers ] = useState(answerResults);
const setAnswer = useCallback((i, answer) => {
const setAnswer = useCallback((i: number, answer: IAnswerData) => {
setAnswers(currentAnswers => {
const newAnswers = [ ...currentAnswers ];
@@ -89,11 +99,15 @@ const AbstractPollCreate = (Component: ComponentType<AbstractProps>) => (props:
}, [ answers ]);
const addAnswer = useCallback((i?: number) => {
const newAnswers: (string | { name: string; voters: Array<string>; })[] = [ ...answers ];
const newAnswers: Array<IAnswerData> = [ ...answers ];
sendAnalytics(createPollEvent('option.added'));
newAnswers.splice(typeof i === 'number' ? i : answers.length, 0, '');
setAnswers(newAnswers as string[]);
newAnswers.splice(typeof i === 'number'
? i : answers.length, 0, {
name: '',
voters: []
});
setAnswers(newAnswers);
}, [ answers ]);
const removeAnswer = useCallback(i => {
@@ -104,7 +118,7 @@ const AbstractPollCreate = (Component: ComponentType<AbstractProps>) => (props:
sendAnalytics(createPollEvent('option.removed'));
newAnswers.splice(i, 1);
setAnswers(newAnswers as string[]);
setAnswers(newAnswers);
}, [ answers ]);
const conference = useSelector((state: IReduxState) => state['features/base/conference'].conference);
@@ -120,7 +134,7 @@ const AbstractPollCreate = (Component: ComponentType<AbstractProps>) => (props:
ev.preventDefault();
}
const filteredAnswers = answers.filter((answer: string) => answer.trim().length > 0);
const filteredAnswers = answers.filter(answer => answer.name.trim().length > 0);
if (filteredAnswers.length < 2) {
return;
@@ -152,7 +166,7 @@ const AbstractPollCreate = (Component: ComponentType<AbstractProps>) => (props:
// Check if the poll create form can be submitted i.e. if the send button should be disabled.
const isSubmitDisabled
= question.trim().length <= 0 // If no question is provided
|| answers.filter((answer: string) => answer.trim().length > 0).length < 2 // If not enough options are provided
|| answers.filter(answer => answer.name.trim().length > 0).length < 2 // If not enough options are provided
|| hasIdenticalAnswers(answers); // If duplicate options are provided
const { t } = useTranslation();

View File

@@ -65,17 +65,16 @@ const AbstractPollResults = (Component: ComponentType<AbstractProps>) => (props:
const answers: Array<AnswerInfo> = useMemo(() => {
const allVoters = new Set();
const pollDetailsAnswers = pollDetails.answers as { name: string; voters: string[]; }[];
// Getting every voters ID that participates to the poll
for (const answer of pollDetailsAnswers) {
for (const answer of pollDetails.answers) {
// checking if the voters is an array for supporting old structure model
const voters: string[] = answer.voters.length ? answer.voters : Object.keys(answer.voters);
voters.forEach((voter: string) => allVoters.add(voter));
}
return pollDetailsAnswers.map(answer => {
return pollDetails.answers.map(answer => {
const nrOfVotersPerAnswer = answer.voters ? Object.keys(answer.voters).length : 0;
const percentage = allVoters.size > 0 ? Math.round(nrOfVotersPerAnswer / allVoters.size * 100) : 0;

View File

@@ -14,7 +14,6 @@ import AbstractPollAnswer, { AbstractProps } from '../AbstractPollAnswer';
import { chatStyles, dialogStyles } from './styles';
const PollAnswer = (props: AbstractProps) => {
const {
checkBoxStates,
@@ -32,7 +31,6 @@ const PollAnswer = (props: AbstractProps) => {
const dispatch = useDispatch();
const localParticipant = useSelector(getLocalParticipant);
const { PRIMARY, SECONDARY } = BUTTON_TYPES;
const pollAnswers = poll.answers as { name: string; voters: string[]; }[];
return (
<>
@@ -43,7 +41,7 @@ const PollAnswer = (props: AbstractProps) => {
</Text>
<View style = { chatStyles.answerContent as ViewStyle }>
{
pollAnswers.map((answer, index: number) => (
poll.answers.map((answer, index: number) => (
<View
key = { index }
style = { chatStyles.switchRow as ViewStyle } >
@@ -52,7 +50,7 @@ const PollAnswer = (props: AbstractProps) => {
disabled = { poll.saved }
onChange = { state => setCheckbox(index, state) } />
<Text style = { chatStyles.switchLabel as TextStyle }>
{ poll.saved ? answer : answer.name }
{ answer.name }
</Text>
</View>
))

View File

@@ -79,7 +79,7 @@ const PollCreate = (props: AbstractProps) => {
// Called on keypress in answer fields
const onAnswerKeyDown = useCallback((index: number, ev) => {
const { key } = ev.nativeEvent;
const currentText = answers[index];
const currentText = answers[index].name;
if (key === 'Backspace' && currentText === '' && answers.length > 1) {
removeAnswer(index);
@@ -110,14 +110,18 @@ const PollCreate = (props: AbstractProps) => {
label = { t('polls.create.pollOption', { index: index + 1 }) }
maxLength = { CHAR_LIMIT }
multiline = { true }
onChange = { text => setAnswer(index, text) }
onChange = { name => setAnswer(index,
{
name,
voters: []
}) }
onKeyPress = { ev => onAnswerKeyDown(index, ev) }
placeholder = { t('polls.create.answerPlaceholder', { index: index + 1 }) }
// This is set to help the touch event not be propagated to any subviews.
pointerEvents = { 'auto' }
ref = { input => registerFieldRef(index, input) }
value = { answers[index] } />
value = { answers[index].name } />
{
answers.length > 2
&& createRemoveOptionButton(() => removeAnswer(index))

View File

@@ -83,7 +83,7 @@ const PollAnswer = ({
</div>
<ul className = { classes.answerList }>
{
poll.answers.map((answer: any, index: number) => (
poll.answers.map((answer, index: number) => (
<li
className = { classes.answer }
key = { index }>
@@ -91,7 +91,7 @@ const PollAnswer = ({
checked = { checkBoxStates[index] }
disabled = { poll.saved }
key = { index }
label = { poll.saved ? answer : answer.name }
label = { answer.name }
onChange = { ev => setCheckbox(index, ev.target.checked) } />
</li>
))

View File

@@ -206,11 +206,12 @@ const PollCreate = ({
value = { question } />
</div>
<ol className = { classes.answerList }>
{answers.map((answer: string, i: number) => {
{answers.map((answer, i: number) => {
const isIdenticalAnswer = answers.slice(0, i).length === 0 ? false
: answers.slice(0, i).some((prevAnswer: string) =>
prevAnswer === answer && prevAnswer !== '' && answer !== '');
: answers.slice(0, i).some(prevAnswer =>
prevAnswer.name === answer.name
&& prevAnswer.name !== '' && answer.name !== '');
return (<li
className = { classes.answer }
@@ -222,12 +223,15 @@ const PollCreate = ({
id = { `polls-answer-input-${i}` }
label = { t('polls.create.pollOption', { index: i + 1 }) }
maxLength = { CHAR_LIMIT }
onChange = { val => setAnswer(i, val) }
onChange = { name => setAnswer(i, {
name,
voters: []
}) }
onKeyPress = { ev => onAnswerKeyDown(i, ev) }
placeholder = { t('polls.create.answerPlaceholder', { index: i + 1 }) }
ref = { r => registerFieldRef(i, r) }
textarea = { true }
value = { answer } />
value = { answer.name } />
{ answers.length > 2
&& <button

View File

@@ -1,5 +1,7 @@
import { IReduxState } from '../app/types';
import { IAnswerData } from './types';
/**
* Selector creator for determining if poll results should be displayed or not.
*
@@ -49,12 +51,12 @@ export function isSubmitAnswerDisabled(checkBoxStates: Array<boolean>) {
/**
* Check if the input array has identical answers.
*
* @param {Array<string>} currentAnswers - The array of current answers to compare.
* @param {Array<IAnswerData>} currentAnswers - The array of current answers to compare.
* @returns {boolean} - Returns true if the answers are identical.
*/
export function hasIdenticalAnswers(currentAnswers: Array<string>): boolean {
export function hasIdenticalAnswers(currentAnswers: Array<IAnswerData>): boolean {
const nonEmptyCurrentAnswers = currentAnswers.filter((answer: string): boolean => answer !== '');
const nonEmptyCurrentAnswers = currentAnswers.filter((answer): boolean => answer.name !== '');
const currentAnswersSet = new Set(nonEmptyCurrentAnswers);

View File

@@ -60,7 +60,6 @@ ReducerRegistry.register<IPollsState>('features/polls', (state = INITIAL_STATE,
...state,
polls: {
...state.polls,
[action.pollId]: action.poll
},
nbUnreadPolls: state.nbUnreadPolls + 1
@@ -81,8 +80,7 @@ ReducerRegistry.register<IPollsState>('features/polls', (state = INITIAL_STATE,
}
// if the poll exists, we update it with the incoming answer
const statePollsAnswers = state.polls[pollId].answers as { name: string; voters: string[]; }[];
const newAnswers = statePollsAnswers
const newAnswers = state.polls[pollId].answers
.map(_answer => {
// checking if the voters is an array for supporting old structure model
const answerVoters = _answer.voters

View File

@@ -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: string[]; }> | string[];
answers: Array<IAnswerData>;
/**
* Whether the poll vote is being edited/changed.
@@ -69,3 +69,16 @@ export interface IPoll {
export interface IPollData extends IPoll {
id: string;
}
export interface IAnswerData {
/**
* The answer name chosen for the poll.
*/
name: string;
/**
* An array of voters.
*/
voters: Array<string>;
}