fix(polls/web) check for duplicate poll options (#14541)

This commit is contained in:
Hazoom
2024-03-26 16:38:22 +02:00
committed by GitHub
parent 9b798b4514
commit bb49c92cc4
59 changed files with 242 additions and 4 deletions

View File

@@ -432,6 +432,11 @@
},
"passwordDigitsOnly": "",
"passwordSetRemotely": "",
"polls": {
"errors": {
"notUniqueOption": "Opsies moet uniek wees"
}
},
"poweredby": "aangedryf deur",
"presenceStatus": {
"busy": "Besig",

View File

@@ -765,6 +765,9 @@
"removeOption": "إزالة خيار",
"send": "أرسل"
},
"errors": {
"notUniqueOption": "يجب أن تكون الخيارات فريدة"
},
"notification": {
"description": "افتح علامة تبويب الاقتراع للتصويت",
"title": "تمت إضافة اقتراع جديد إلى هذا المُلتقى"

View File

@@ -478,6 +478,11 @@
},
"passwordDigitsOnly": "Да {{number}} лічбаў",
"passwordSetRemotely": "устаноўлены іншым удзельнікам",
"polls": {
"errors": {
"notUniqueOption": "Варыянты павінны быць унікальнымі"
}
},
"poweredby": "працуе на",
"presenceStatus": {
"busy": "Заняты",

View File

@@ -527,6 +527,11 @@
},
"passwordDigitsOnly": "До {{number}} цифри",
"passwordSetRemotely": "зададена от друг участник",
"polls": {
"errors": {
"notUniqueOption": "Опциите трябва да са уникални"
}
},
"poweredby": "с подкрепата на",
"presenceStatus": {
"busy": "Зает",

View File

@@ -772,6 +772,9 @@
"removeOption": "Elimina l'opció",
"send": "Envia"
},
"errors": {
"notUniqueOption": "Les opcions han de ser úniques"
},
"notification": {
"description": "Obre la pestanya de les enquestes per a votar",
"title": "S'ha afegit una nova enquesta en aquesta reunió"

View File

@@ -747,6 +747,9 @@
"removeOption": "",
"send": ""
},
"errors": {
"notUniqueOption": "Možnosti musí být jedinečné"
},
"notification": {
"description": "",
"title": ""

View File

@@ -464,6 +464,11 @@
},
"passwordDigitsOnly": "Op til {{number}} tal",
"passwordSetRemotely": "Sat af et andet medlem",
"polls": {
"errors": {
"notUniqueOption": "Valgmulighederne skal være unikke"
}
},
"poweredby": "Powered by",
"presenceStatus": {
"busy": "Optaget",

View File

@@ -867,6 +867,9 @@
"removeOption": "Antwort entfernen",
"send": "Erstellen"
},
"errors": {
"notUniqueOption": "Optionen müssen einzigartig sein"
},
"notification": {
"description": "Öffnen Sie das Umfragen-Tab um abzustimmen",
"title": "Dieser Konferenz wurde eine Umfrage hinzugefügt"

View File

@@ -775,6 +775,9 @@
"removeOption": "wótegrono wulašowaś",
"send": "wótpósłaś"
},
"errors": {
"notUniqueOption": "Opcije musy byś jedynsće"
},
"notification": {
"description": "Wótcyńśo kórtu wopšašowanjow, aby zgłosowali",
"title": "Za tu konferencu jo nowe wopšašowanje pśigótowane"

View File

@@ -792,6 +792,9 @@
"removeOption": "Αφαιρέστε την επιλογή",
"send": "Αποστολή"
},
"errors": {
"notUniqueOption": "Οι επιλογές πρέπει να είναι μοναδικές"
},
"notification": {
"description": "Ανοίξτε τη σελίδα ψηφοφοριών για να ψηφίσετε",
"title": "Μια νέα ψηφοφορία προστέθηκε στη σύσκεψη"

View File

@@ -864,6 +864,9 @@
"removeOption": "Forigi opcion",
"send": "Sendu"
},
"errors": {
"notUniqueOption": "Ebloj devas esti unikaj"
},
"notification": {
"description": "Malfermu la enketan langeton por voĉdoni",
"title": "Oni aldonis novan enketon en la kunveno"

View File

@@ -815,6 +815,9 @@
"removeOption": "Eliminar la opción",
"send": "Enviar"
},
"errors": {
"notUniqueOption": "Las opciones deben ser únicas"
},
"notification": {
"description": "Abre la pestaña de encuestas para votar",
"title": "Se ha añadido una nueva encuesta a esta reunión"

View File

@@ -691,6 +691,9 @@
"removeOption": "Eliminar la opción",
"send": "Enviar"
},
"errors": {
"notUniqueOption": "Las opciones deben ser únicas"
},
"notification": {
"description": "Abre la pestaña de encuestas para votar",
"title": "Se ha añadido una nueva encuesta a esta reunión"

View File

@@ -467,6 +467,11 @@
},
"passwordDigitsOnly": "Kuni {{number}} tähemärki",
"passwordSetRemotely": "määratud teise kasutaja poolt",
"polls": {
"errors": {
"notUniqueOption": "Valikud peavad olema ainulaadsed"
}
},
"poweredby": "teieni toodud",
"presenceStatus": {
"busy": "Hõivatud",

View File

@@ -588,6 +588,11 @@
},
"passwordDigitsOnly": "{{number}} digitu arte",
"passwordSetRemotely": "beste parte-hartzaile batek ezarrita",
"polls": {
"errors": {
"notUniqueOption": "Aukerak bakarrak izan behar dira"
}
},
"poweredby": "garatzailea:",
"prejoin": {
"audioAndVideoError": "Errorea audio eta bideoan:",

View File

@@ -823,6 +823,9 @@
"removeOption": "حذف گزینه",
"send": "ارسال"
},
"errors": {
"notUniqueOption": "گزینه ها باید منحصر به فرد باشند"
},
"notification": {
"description": "برای رأی‌دادن، زبانهٔ نظرسنجی‌ها را باز کنید",
"title": "نظرسنجی جدیدی به این جلسه اضافه شد"

View File

@@ -438,6 +438,11 @@
},
"passwordDigitsOnly": "",
"passwordSetRemotely": "",
"polls": {
"errors": {
"notUniqueOption": "Vaihtoehtojen on oltava ainutlaatuisia"
}
},
"poweredby": "tukija:",
"presenceStatus": {
"busy": "Varattu",

View File

@@ -865,6 +865,9 @@
"removeOption": "Supprimer l'option",
"send": "Envoyer"
},
"errors": {
"notUniqueOption": "Les options doivent être uniques"
},
"notification": {
"description": "Ouvrez l'onglet des sondages pour voter",
"title": "Un nouveau sondage a été ajouté à la réunion"

View File

@@ -449,6 +449,11 @@
},
"passwordDigitsOnly": "Jusqu'à {{number}} chiffres",
"passwordSetRemotely": "réglé par un autre membre",
"polls": {
"errors": {
"notUniqueOption": "Les options doivent être uniques"
}
},
"poweredby": "optimisé par",
"presenceStatus": {
"busy": "Occupé",

View File

@@ -454,6 +454,11 @@
},
"passwordDigitsOnly": "Ata {{number}} díxitos",
"passwordSetRemotely": "estabelecida por outro participante",
"polls": {
"errors": {
"notUniqueOption": "As opcións deben ser únicas"
}
},
"poweredby": "fornecido por",
"presenceStatus": {
"busy": "Ocupado",

View File

@@ -477,6 +477,11 @@
},
"passwordDigitsOnly": "עד {{number}} ספרות",
"passwordSetRemotely": "נקבע על ידי חבר אחר",
"polls": {
"errors": {
"notUniqueOption": "האפשרויות חייבות להיות ייחודיות"
}
},
"poweredby": "מופעל על ידי",
"presenceStatus": {
"busy": "עסוק",

View File

@@ -565,6 +565,11 @@
},
"passwordDigitsOnly": "Up to {{number}} digits",
"passwordSetRemotely": "दूसरे प्रतिभागी द्वारा निर्धारित",
"polls": {
"errors": {
"notUniqueOption": "विकल्प अद्वितीय होना चाहिए"
}
},
"poweredby": "powered by",
"prejoin": {
"audioAndVideoError": "ऑडियो और वीडियो त्रुटि:",

View File

@@ -770,6 +770,9 @@
"removeOption": "Ukloni opciju",
"send": "Pošalji"
},
"errors": {
"notUniqueOption": "Opcije moraju biti jedinstvene"
},
"notification": {
"description": "Za glasanje otvori karticu ankete",
"title": "Ovom sastanku je dodana nova anketa"

View File

@@ -755,6 +755,9 @@
"removeOption": "wotmołwu wotstronić",
"send": "zestajić"
},
"errors": {
"notUniqueOption": "opcije dyrbja jasne być"
},
"notification": {
"description": "Za wobdźělenje wočińće tab za naprašowanje.",
"title": "Tutej konferency bu naprašowanje přidate."

View File

@@ -623,6 +623,9 @@
"removeOption": "Opció eltávolítása",
"send": "Küldés"
},
"errors": {
"notUniqueOption": "Az opcióknak egyedinek kell lenniük"
},
"notification": {
"description": "Szavazás megnyitása",
"title": "Új szavazás létrehozva"

View File

@@ -427,6 +427,11 @@
},
"passwordDigitsOnly": "",
"passwordSetRemotely": "Սահմանվել է մեկ այլ մասնակցի կողմից",
"polls": {
"errors": {
"notUniqueOption": "Ընտրանքները պետք է լինեն եզակի"
}
},
"poweredby": "Հիմնված է",
"presenceStatus": {
"busy": "",

View File

@@ -865,6 +865,9 @@
"removeOption": "Fjarlægja valkost",
"send": "Senda"
},
"errors": {
"notUniqueOption": "Valkostir hljóta að vera einstök"
},
"notification": {
"description": "Opnaðu könnunarflipann til að greiða atkvæði",
"title": "Nýrri könnun var bætt á þennan fund"

View File

@@ -770,6 +770,9 @@
"removeOption": "Elimina risposta",
"send": "Invia"
},
"errors": {
"notUniqueOption": "Le opzioni devono essere uniche"
},
"notification": {
"description": "Apri la scheda sondaggi per votare",
"title": "Un nuovo sondaggio è stato aggiunto alla riunione"

View File

@@ -715,6 +715,9 @@
"removeOption": "選択肢の削除",
"send": "送信"
},
"errors": {
"notUniqueOption": "オプションは一意でなければなりません"
},
"notification": {
"description": "投票するには投票タブを開いてください",
"title": "新しい投票がこのミーティングに追加されました"

View File

@@ -667,6 +667,9 @@
"removeOption": "Kkes aɣewwaṛ",
"send": "Azen"
},
"errors": {
"notUniqueOption": "tifranin ilaq ad ilin d imaynuten"
},
"notification": {
"description": "Ldi iccer n yisenqad i ufran",
"title": "Asenqed amaynut yettwarna ɣer temlilt-a"

View File

@@ -497,6 +497,11 @@
},
"passwordDigitsOnly": "최대 {{number}} 자리",
"passwordSetRemotely": "다른 참가자가 설정",
"polls": {
"errors": {
"notUniqueOption": "옵션은 고유해야합니다"
}
},
"poweredby": "powered by",
"presenceStatus": {
"busy": "바쁨",

View File

@@ -467,6 +467,11 @@
},
"passwordDigitsOnly": "Daugiausia {{number}} skaičių",
"passwordSetRemotely": "nustatytas kito naudotojo",
"polls": {
"errors": {
"notUniqueOption": "Parinktys turi būti unikalios"
}
},
"poweredby": "pateikiamas",
"presenceStatus": {
"busy": "Užimtas",

View File

@@ -875,6 +875,9 @@
"removeOption": "Noņemt opciju",
"send": "Nosūtīt"
},
"errors": {
"notUniqueOption": "Iespējām jābūt unikālām"
},
"notification": {
"description": "Lai balsotu, atveriet aptauju cilni",
"title": "Šai sapulcei tika pievienota jauna aptauja"

View File

@@ -545,6 +545,11 @@
},
"passwordDigitsOnly": "{{number}} അക്കങ്ങൾ വരെ",
"passwordSetRemotely": "മറ്റൊരു പങ്കാളി സജ്ജമാക്കിയത്",
"polls": {
"errors": {
"notUniqueOption": "ഓപ്ഷനുകൾ അദ്വിതീയമായിരിക്കണം"
}
},
"poweredby": "powered by",
"prejoin": {
"audioAndVideoError": "ഓഡിയോ, വീഡിയോ പിശക്:",

View File

@@ -802,6 +802,9 @@
"removeOption": "Сонголт хасах",
"send": "Илгээх"
},
"errors": {
"notUniqueOption": "Сонголтууд өвөрмөц байх ёстой"
},
"notification": {
"description": "Саналаа өгөхийн тулд санал асуулгын хавтсыг нээнэ үү",
"title": "Уулзалтанд шинэ санал асуулга нэмэгдлээ"

View File

@@ -482,6 +482,11 @@
},
"passwordDigitsOnly": " पर्यंत {{number}} अंक",
"passwordSetRemotely": "दुसर्‍या सहभागीने सेट केलेले",
"polls": {
"errors": {
"notUniqueOption": "पर्याय अद्वितीय असणे आवश्यक आहे"
}
},
"poweredby": "द्वारा समर्थित",
"prejoin": {
"audioAndVideoError": "ऑडिओ आणि व्हिडिओ त्रुटी:",

View File

@@ -668,6 +668,9 @@
"removeOption": "Verwijder optie",
"send": "Verstuur"
},
"errors": {
"notUniqueOption": "Opties moeten uniek zijn"
},
"notification": {
"description": "Open het peilingen tabblad om te stemmen",
"title": "Een nieuwe peiling is aangemaakt in deze vergadering"

View File

@@ -714,6 +714,9 @@
"removeOption": "Suprimir l'opcion",
"send": "Enviar"
},
"errors": {
"notUniqueOption": "Las opcions devon èsser unicas"
},
"notification": {
"description": "Dobrissètz longlet dels sondatge per votar",
"title": "Un sondatge novèl es estat apondut a la conferéncia"

View File

@@ -803,6 +803,9 @@
"removeOption": "Usuń opcję",
"send": "Wyślij"
},
"errors": {
"notUniqueOption": "Opcje muszą być wyjątkowe"
},
"notification": {
"description": "Otwórz kartę ankiet, aby zagłosować",
"title": "Utworzono nową ankietę do tego spotkania"

View File

@@ -875,6 +875,9 @@
"removeOption": "Remover opção",
"send": "Enviar"
},
"errors": {
"notUniqueOption": "As opções devem ser únicas"
},
"notification": {
"description": "Abrir o separador das sondagens para votar",
"title": "Uma nova sondagem foi adicionada a esta reunião"

View File

@@ -864,6 +864,9 @@
"removeOption": "Remover opção",
"send": "Enviar"
},
"errors": {
"notUniqueOption": "As opções devem ser exclusivas"
},
"notification": {
"description": "Abra a aba das votações para votar",
"title": "Uma nova votação foi iniciada nesta conferência"

View File

@@ -471,6 +471,11 @@
},
"passwordDigitsOnly": "Până la {{number}} cifre",
"passwordSetRemotely": "Setată de un alt membru",
"polls": {
"errors": {
"notUniqueOption": "Opțiunile trebuie să fie unice"
}
},
"poweredby": "cu sprijinul",
"presenceStatus": {
"busy": "Ocupat",

View File

@@ -771,6 +771,9 @@
"removeOption": "Удалить вариант",
"send": "Отправлять"
},
"errors": {
"notUniqueOption": "Варианты должны быть уникальными"
},
"notification": {
"description": "Откройте вкладку опросов, чтобы проголосовать",
"title": "К этой встрече добавлен новый опрос"

View File

@@ -772,6 +772,9 @@
"removeOption": "Boga s'optzione",
"send": "Imbia"
},
"errors": {
"notUniqueOption": "Sas optziones depent èssere ùnicas"
},
"notification": {
"description": "Aberi ischeda de sondàgiu pro votare",
"title": "Sondàgiu nou agiuntu a sa riunione"

View File

@@ -547,6 +547,11 @@
},
"passwordDigitsOnly": "až {{number}} číslic",
"passwordSetRemotely": "nastavené iným účastníkom",
"polls": {
"errors": {
"notUniqueOption": "Možnosti musia byť jedinečné"
}
},
"poweredby": "založené na",
"prejoin": {
"audioAndVideoError": "Chyba zvuku a videa:",

View File

@@ -668,6 +668,9 @@
"removeOption": "Odstrani možnost",
"send": "Pošlji"
},
"errors": {
"notUniqueOption": "Možnosti morajo biti edinstvene"
},
"notification": {
"description": "Odpri zavihek z anketami za glasovanje",
"title": "V sestanek je bila dodana nova anketa"

View File

@@ -872,6 +872,9 @@
"removeOption": "Hiqe mundësinë",
"send": "Dërgoje"
},
"errors": {
"notUniqueOption": "Opsionet duhet të jenë unike"
},
"notification": {
"description": "Që të votoni, hapni skedën e pyetësorëve",
"title": "Te ky takim u shtua një pyetësor i ri"

View File

@@ -447,6 +447,11 @@
},
"passwordDigitsOnly": "",
"passwordSetRemotely": "",
"polls": {
"errors": {
"notUniqueOption": "Опције морају бити јединствене"
}
},
"poweredby": "",
"prejoin": {
"audioAndVideoError": "Грешка звука и видеа:",

View File

@@ -804,6 +804,9 @@
"removeOption": "Ta bort alternativ",
"send": "Skicka"
},
"errors": {
"notUniqueOption": "Alternativ måste vara unika"
},
"notification": {
"description": "Öppna fliken omröstningar för att rösta",
"title": "En ny omröstning har blivit tillagd till detta möte"

View File

@@ -577,6 +577,11 @@
},
"passwordDigitsOnly": "{{number}} అంకెల వరకు",
"passwordSetRemotely": "మరో సదస్యులు అమర్చారు",
"polls": {
"errors": {
"notUniqueOption": "ఎంపికలు ప్రత్యేకంగా ఉండాలి"
}
},
"poweredby": "శక్తిమంతం",
"prejoin": {
"audioAndVideoError": "Audio and video error:",

View File

@@ -875,6 +875,9 @@
"removeOption": "Seçeneği sil",
"send": "Gönder"
},
"errors": {
"notUniqueOption": "Seçenekler benzersiz olmalı"
},
"notification": {
"description": "Oy vermek için anketler sekmesini açın",
"title": "Anket toplantıya eklendi"

View File

@@ -800,6 +800,9 @@
"removeOption": "Вилучити",
"send": "Надіслати"
},
"errors": {
"notUniqueOption": "Параметри повинні бути унікальними"
},
"notification": {
"description": "Щоб проголосувати, відкрийте вкладку опитувань",
"title": "Додано нове опитування"

View File

@@ -474,6 +474,11 @@
},
"passwordDigitsOnly": "",
"passwordSetRemotely": "được thiết lập bởi một người khác",
"polls": {
"errors": {
"notUniqueOption": "Tùy chọn phải là duy nhất"
}
},
"poweredby": "Được hỗ trợ bởi",
"presenceStatus": {
"busy": "Bận",

View File

@@ -845,6 +845,9 @@
"removeOption": "移除选项",
"send": "发送"
},
"errors": {
"notUniqueOption": "选项必须是唯一的"
},
"notification": {
"description": "打开投票页面进行投票",
"title": "本次会议有一项新的投票"

View File

@@ -863,6 +863,9 @@
"removeOption": "移除選項",
"send": "傳送"
},
"errors": {
"notUniqueOption": "選項必須是唯一的"
},
"notification": {
"description": "開啟投票分頁以參與投票",
"title": "此會議有一項新投票"

View File

@@ -875,6 +875,9 @@
"removeOption": "Remove option",
"send": "Send"
},
"errors": {
"notUniqueOption": "Options must be unique"
},
"notification": {
"description": "Open polls tab to vote",
"title": "A new poll was added to this meeting"

View File

@@ -6,6 +6,7 @@ import { createPollEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IReduxState } from '../../app/types';
import { COMMAND_NEW_POLL } from '../constants';
import { hasIdenticalAnswers } from '../functions';
/**
* The type of the React {@code Component} props of inheriting component.
@@ -37,6 +38,8 @@ export type AbstractProps = InputProps & {
* @param {React.AbstractComponent} Component - The concrete component.
* @returns {React.AbstractComponent}
*/
const AbstractPollCreate = (Component: ComponentType<AbstractProps>) => (props: InputProps) => {
const { setCreateMode } = props;
@@ -102,7 +105,8 @@ 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 => answer.trim().length > 0).length < 2; // If not enough options are provided
|| answers.filter(answer => answer.trim().length > 0).length < 2 // If not enough options are provided
|| hasIdenticalAnswers(answers); // If duplicate options are provided
const { t } = useTranslation();

View File

@@ -201,11 +201,19 @@ const PollCreate = ({
value = { question } />
</div>
<ol className = { classes.answerList }>
{answers.map((answer: any, i: number) =>
(<li
{answers.map((answer: any, i: number) => {
const isIdenticalAnswer = answers.slice(0, i).length === 0 ? false
: answers.slice(0, i).some((prevAnswer: string) =>
prevAnswer === answer && prevAnswer !== '' && answer !== '');
return (<li
className = { classes.answer }
key = { i }>
<Input
bottomLabel = { (isIdenticalAnswer ? t('polls.errors.notUniqueOption',
{ index: i + 1 }) : '') }
error = { isIdenticalAnswer }
id = { `polls-answer-input-${i}` }
label = { t('polls.create.pollOption', { index: i + 1 }) }
maxLength = { CHAR_LIMIT }
@@ -223,7 +231,8 @@ const PollCreate = ({
type = 'button'>
{ t('polls.create.removeOption') }
</button>}
</li>)
</li>);
}
)}
</ol>
<div className = { classes.addButtonContainer }>

View File

@@ -45,3 +45,18 @@ export function getUnreadPollCount(state: IReduxState) {
export function isSubmitAnswerDisabled(checkBoxStates: Array<boolean>) {
return !checkBoxStates.find(checked => checked);
}
/**
* Check if the input array has identical answers.
*
* @param {Array<string>} currentAnswers - The array of current answers to compare.
* @returns {boolean} - Returns true if the answers are identical.
*/
export function hasIdenticalAnswers(currentAnswers: Array<string>): boolean {
const nonEmptyCurrentAnswers = currentAnswers.filter((answer: string): boolean => answer !== '');
const currentAnswersSet = new Set(nonEmptyCurrentAnswers);
return currentAnswersSet.size !== nonEmptyCurrentAnswers.length;
}