Skip to content

Commit 7998e6c

Browse files
authored
Add byte length checks to poll question and options during create
1 parent 92f253f commit 7998e6c

File tree

4 files changed

+47
-11
lines changed

4 files changed

+47
-11
lines changed

_locales/en/messages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,14 @@
15341534
"messageformat": "Poll requires at least 2 options",
15351535
"description": "Error message shown when poll has fewer than 2 non-empty options"
15361536
},
1537+
"icu:PollCreateModal__Error--QuestionTooLong": {
1538+
"messageformat": "Question is too long",
1539+
"description": "Error message shown when poll question string exceeds the byte limit"
1540+
},
1541+
"icu:PollCreateModal__Error--OptionTooLong": {
1542+
"messageformat": "Option is too long",
1543+
"description": "Error message shown when poll option string exceeds the byte limit"
1544+
},
15371545
"icu:deleteConversation": {
15381546
"messageformat": "Delete",
15391547
"description": "Menu item for deleting a conversation (including messages), title case."

ts/components/PollCreateModal.dom.tsx

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
POLL_OPTIONS_MAX_COUNT,
2424
} from '../types/Polls.dom.js';
2525
import { count as countGraphemes } from '../util/grapheme.std.js';
26+
import { MAX_MESSAGE_BODY_BYTE_LENGTH } from '../util/longAttachment.std.js';
2627

2728
type PollOption = {
2829
id: string;
@@ -212,20 +213,34 @@ export function PollCreateModal({
212213
hasQuestionError: boolean;
213214
hasOptionsError: boolean;
214215
} => {
215-
const errors: Array<string> = [];
216-
const hasQuestionError = !question.trim();
217-
const nonEmptyOptions = options.filter(opt => opt.value.trim());
218-
const hasOptionsError = nonEmptyOptions.length < POLL_OPTIONS_MIN_COUNT;
216+
const questionErrors: Array<string> = [];
217+
const optionErrors: Array<string> = [];
219218

220-
if (hasQuestionError) {
221-
errors.push(i18n('icu:PollCreateModal__Error--RequiresQuestion'));
219+
const questionValue = question.trim();
220+
if (!questionValue) {
221+
questionErrors.push(i18n('icu:PollCreateModal__Error--RequiresQuestion'));
222+
}
223+
if (Buffer.byteLength(questionValue) > MAX_MESSAGE_BODY_BYTE_LENGTH) {
224+
questionErrors.push(i18n('icu:PollCreateModal__Error--QuestionTooLong'));
222225
}
223226

224-
if (hasOptionsError) {
225-
errors.push(i18n('icu:PollCreateModal__Error--RequiresTwoOptions'));
227+
const optionValues = options.map(opt => opt.value.trim());
228+
const nonEmptyOptions = optionValues.filter(value => value);
229+
if (nonEmptyOptions.length < POLL_OPTIONS_MIN_COUNT) {
230+
optionErrors.push(i18n('icu:PollCreateModal__Error--RequiresTwoOptions'));
231+
}
232+
const optionOverByteLength = optionValues.find(
233+
value => Buffer.byteLength(value) > MAX_MESSAGE_BODY_BYTE_LENGTH
234+
);
235+
if (optionOverByteLength) {
236+
optionErrors.push(i18n('icu:PollCreateModal__Error--OptionTooLong'));
226237
}
227238

228-
return { errors, hasQuestionError, hasOptionsError };
239+
return {
240+
errors: questionErrors.concat(optionErrors),
241+
hasQuestionError: questionErrors.length > 0,
242+
hasOptionsError: optionErrors.length > 0,
243+
};
229244
}, [question, options, i18n]);
230245

231246
const handleSend = useCallback(() => {

ts/types/Polls.dom.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import * as RemoteConfig from '../RemoteConfig.dom.js';
1212
import { isAlpha, isBeta, isProduction } from '../util/version.std.js';
1313
import type { SendStateByConversationId } from '../messages/MessageSendState.std.js';
1414
import { aciSchema } from './ServiceId.std.js';
15+
import { MAX_MESSAGE_BODY_BYTE_LENGTH } from '../util/longAttachment.std.js';
1516

1617
export const POLL_QUESTION_MAX_LENGTH = 100;
1718
export const POLL_OPTIONS_MIN_COUNT = 2;
@@ -28,7 +29,13 @@ export const PollCreateSchema = z
2829
.min(1)
2930
.refine(value => hasAtMostGraphemes(value, POLL_QUESTION_MAX_LENGTH), {
3031
message: `question must contain at most ${POLL_QUESTION_MAX_LENGTH} characters`,
31-
}),
32+
})
33+
.refine(
34+
value => Buffer.byteLength(value) <= MAX_MESSAGE_BODY_BYTE_LENGTH,
35+
{
36+
message: `question must contain at most ${MAX_MESSAGE_BODY_BYTE_LENGTH} bytes`,
37+
}
38+
),
3239
options: z
3340
.array(
3441
z
@@ -40,6 +47,12 @@ export const PollCreateSchema = z
4047
message: `option must contain at most ${POLL_QUESTION_MAX_LENGTH} characters`,
4148
}
4249
)
50+
.refine(
51+
value => Buffer.byteLength(value) <= MAX_MESSAGE_BODY_BYTE_LENGTH,
52+
{
53+
message: `option must contain at most ${MAX_MESSAGE_BODY_BYTE_LENGTH} bytes`,
54+
}
55+
)
4356
)
4457
.min(POLL_OPTIONS_MIN_COUNT)
4558
.max(POLL_OPTIONS_MAX_COUNT)

ts/util/longAttachment.std.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { unicodeSlice } from './unicodeSlice.std.js';
55

66
const KIBIBYTE = 1024;
7-
const MAX_MESSAGE_BODY_BYTE_LENGTH = 2 * KIBIBYTE;
7+
export const MAX_MESSAGE_BODY_BYTE_LENGTH = 2 * KIBIBYTE;
88

99
export const MAX_BODY_ATTACHMENT_BYTE_LENGTH = 64 * KIBIBYTE;
1010

0 commit comments

Comments
 (0)