@@ -28,10 +28,30 @@ const double _composeButtonSize = 44;
28
28
///
29
29
/// Subclasses must ensure that [_update] is called in all exposed constructors.
30
30
abstract class ComposeController <ErrorT > extends TextEditingController {
31
+ int get maxLengthUnicodeCodePoints;
32
+
31
33
String get textNormalized => _textNormalized;
32
34
late String _textNormalized;
33
35
String _computeTextNormalized ();
34
36
37
+ /// Length of [textNormalized] in Unicode code points,
38
+ /// if it might exceed [maxLengthUnicodeCodePoints] , else null.
39
+ ///
40
+ /// Use this instead of [String.length]
41
+ /// to enforce a max length expressed in code points.
42
+ /// [String.length] is conservative and may cut the user off too short.
43
+ ///
44
+ /// Counting code points ([String.runes] )
45
+ /// is more expensive than getting the number of UTF-16 code units
46
+ /// ([String.length] ), so we avoid it when the result definitely won't exceed
47
+ /// [maxLengthUnicodeCodePoints] .
48
+ int ? get lengthUnicodeCodePointsIfLong => _lengthUnicodeCodePointsIfLong;
49
+ late int ? _lengthUnicodeCodePointsIfLong;
50
+ int ? _computeLengthUnicodeCodePointsIfLong () =>
51
+ _textNormalized.length > maxLengthUnicodeCodePoints
52
+ ? _textNormalized.runes.length
53
+ : null ;
54
+
35
55
List <ErrorT > get validationErrors => _validationErrors;
36
56
late List <ErrorT > _validationErrors;
37
57
List <ErrorT > _computeValidationErrors ();
@@ -40,6 +60,8 @@ abstract class ComposeController<ErrorT> extends TextEditingController {
40
60
41
61
void _update () {
42
62
_textNormalized = _computeTextNormalized ();
63
+ // uses _textNormalized, so comes after _computeTextNormalized()
64
+ _lengthUnicodeCodePointsIfLong = _computeLengthUnicodeCodePointsIfLong ();
43
65
_validationErrors = _computeValidationErrors ();
44
66
hasValidationErrors.value = _validationErrors.isNotEmpty;
45
67
}
@@ -74,6 +96,9 @@ class ComposeTopicController extends ComposeController<TopicValidationError> {
74
96
// https://zulip.com/help/require-topics
75
97
final mandatory = true ;
76
98
99
+ // TODO(#307) use `max_topic_length` instead of hardcoded limit
100
+ @override final maxLengthUnicodeCodePoints = kMaxTopicLengthCodePoints;
101
+
77
102
@override
78
103
String _computeTextNormalized () {
79
104
String trimmed = text.trim ();
@@ -86,11 +111,10 @@ class ComposeTopicController extends ComposeController<TopicValidationError> {
86
111
if (mandatory && textNormalized == kNoTopicTopic)
87
112
TopicValidationError .mandatoryButEmpty,
88
113
89
- // textNormalized.length is the number of UTF-16 code units, while the server
90
- // API expresses the max in Unicode code points. So this comparison will
91
- // be conservative and may cut the user off shorter than necessary.
92
- // TODO(#1238) stop cutting off shorter than necessary
93
- if (textNormalized.length > kMaxTopicLengthCodePoints)
114
+ if (
115
+ lengthUnicodeCodePointsIfLong != null
116
+ && lengthUnicodeCodePointsIfLong! > maxLengthUnicodeCodePoints
117
+ )
94
118
TopicValidationError .tooLong,
95
119
];
96
120
}
@@ -121,6 +145,9 @@ class ComposeContentController extends ComposeController<ContentValidationError>
121
145
_update ();
122
146
}
123
147
148
+ // TODO(#1237) use `max_message_length` instead of hardcoded limit
149
+ @override final maxLengthUnicodeCodePoints = kMaxMessageLengthCodePoints;
150
+
124
151
int _nextQuoteAndReplyTag = 0 ;
125
152
int _nextUploadTag = 0 ;
126
153
@@ -262,11 +289,10 @@ class ComposeContentController extends ComposeController<ContentValidationError>
262
289
if (textNormalized.isEmpty)
263
290
ContentValidationError .empty,
264
291
265
- // normalized.length is the number of UTF-16 code units, while the server
266
- // API expresses the max in Unicode code points. So this comparison will
267
- // be conservative and may cut the user off shorter than necessary.
268
- // TODO(#1238) stop cutting off shorter than necessary
269
- if (textNormalized.length > kMaxMessageLengthCodePoints)
292
+ if (
293
+ lengthUnicodeCodePointsIfLong != null
294
+ && lengthUnicodeCodePointsIfLong! > maxLengthUnicodeCodePoints
295
+ )
270
296
ContentValidationError .tooLong,
271
297
272
298
if (_quoteAndReplies.isNotEmpty)
0 commit comments