mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 03:12:29 +00:00
feat(polls/web/native): refactoring (#14778)
* feat(polls/web/native): refactoring
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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>
|
||||
))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user