Skip to content

Commit 2a2a17b

Browse files
committed
compose: Change content input hint text if topic is vacuous and mandatory
Previously, "Message #stream > (no topic)" would appear as the hint text when the topic input is empty but mandatory. The control flow of `getDestinationString` can be simplified. However, it is structured this way to prepare for a later change to support showing "general chat" in the hint text, that adds some more advanced checks. Signed-off-by: Zixuan James Li <[email protected]>
1 parent e584b97 commit 2a2a17b

File tree

2 files changed

+68
-10
lines changed

2 files changed

+68
-10
lines changed

lib/widgets/compose_box.dart

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,30 @@ class ComposeTopicController extends ComposeController<TopicValidationError> {
167167
/// that certain strings are not empty but also indicate the absence of a topic.
168168
bool get isTopicVacuous => textNormalized == kNoTopicTopic;
169169

170+
/// The send destination as a string.
171+
///
172+
/// This returns a string formatted like "#stream name" when topics are
173+
/// mandatory but [textNormalized] is vacuous (see [isTopicVacuous]).
174+
///
175+
/// Otherwise, returns a string formatted like "#stream name > topic name".
176+
// No i18n of the use of "#" and ">" strings; those are part of how
177+
// Zulip expresses channels and topics, not any normal English punctuation,
178+
// so don't make sense to translate. See:
179+
// https://github.com/zulip/zulip-flutter/pull/1148#discussion_r1941990585
180+
String getDestinationString({required String streamName}) {
181+
if (!isTopicVacuous) {
182+
return '#$streamName > $textNormalized';
183+
}
184+
185+
// Sending to a vacuous topic (see [isTopicVacuous]) is not possible if
186+
// topics are [mandatory].
187+
if (mandatory) {
188+
return '#$streamName';
189+
}
190+
191+
return '#$streamName > $kNoTopicTopic';
192+
}
193+
170194
@override
171195
List<TopicValidationError> _computeValidationErrors() {
172196
return [
@@ -585,17 +609,13 @@ class _StreamContentInputState extends State<_StreamContentInput> {
585609
final zulipLocalizations = ZulipLocalizations.of(context);
586610
final streamName = store.streams[widget.narrow.streamId]?.name
587611
?? zulipLocalizations.unknownChannelName;
588-
final topic = TopicName(widget.controller.topic.textNormalized);
589612
return _ContentInput(
590613
narrow: widget.narrow,
591-
destination: TopicNarrow(widget.narrow.streamId, topic),
614+
destination: TopicNarrow(widget.narrow.streamId,
615+
TopicName(widget.controller.topic.textNormalized)),
592616
controller: widget.controller,
593617
hintText: zulipLocalizations.composeBoxChannelContentHint(
594-
// No i18n of this use of "#" and ">" string; those are part of how
595-
// Zulip expresses channels and topics, not any normal English punctuation,
596-
// so don't make sense to translate. See:
597-
// https://github.com/zulip/zulip-flutter/pull/1148#discussion_r1941990585
598-
'#$streamName > ${topic.displayName}'));
618+
widget.controller.topic.getDestinationString(streamName: streamName)));
599619
}
600620
}
601621

test/widgets/compose_box_test.dart

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,18 +353,56 @@ void main() {
353353
.decoration.isNotNull().hintText.equals(contentHintText);
354354
}
355355

356-
group('to ChannelNarrow', () {
356+
group('to ChannelNarrow, topics not mandatory', () {
357357
final narrow = ChannelNarrow(channel.streamId);
358358

359359
testWidgets('with empty topic', (tester) async {
360-
await prepare(tester, narrow: narrow);
360+
await prepare(tester, narrow: narrow, mandatoryTopics: false);
361361
checkComposeBoxHintTexts(tester,
362362
topicHintText: 'Topic',
363363
contentHintText: 'Message #${channel.name} > (no topic)');
364364
});
365365

366+
testWidgets('with non-empty but vacuous topic', (tester) async {
367+
await prepare(tester, narrow: narrow, mandatoryTopics: false);
368+
await enterTopic(tester, narrow: narrow, topic: '(no topic)');
369+
await tester.pump();
370+
checkComposeBoxHintTexts(tester,
371+
topicHintText: 'Topic',
372+
contentHintText: 'Message #${channel.name} > (no topic)');
373+
});
374+
375+
testWidgets('with non-empty topic', (tester) async {
376+
await prepare(tester, narrow: narrow, mandatoryTopics: false);
377+
await enterTopic(tester, narrow: narrow, topic: 'new topic');
378+
await tester.pump();
379+
checkComposeBoxHintTexts(tester,
380+
topicHintText: 'Topic',
381+
contentHintText: 'Message #${channel.name} > new topic');
382+
});
383+
});
384+
385+
group('to ChannelNarrow, mandatory topics', () {
386+
final narrow = ChannelNarrow(channel.streamId);
387+
388+
testWidgets('with empty topic', (tester) async {
389+
await prepare(tester, narrow: narrow, mandatoryTopics: true);
390+
checkComposeBoxHintTexts(tester,
391+
topicHintText: 'Topic',
392+
contentHintText: 'Message #${channel.name}');
393+
});
394+
395+
testWidgets('with non-empty but vacuous topic', (tester) async {
396+
await prepare(tester, narrow: narrow, mandatoryTopics: true);
397+
await enterTopic(tester, narrow: narrow, topic: '(no topic)');
398+
await tester.pump();
399+
checkComposeBoxHintTexts(tester,
400+
topicHintText: 'Topic',
401+
contentHintText: 'Message #${channel.name}');
402+
});
403+
366404
testWidgets('with non-empty topic', (tester) async {
367-
await prepare(tester, narrow: narrow);
405+
await prepare(tester, narrow: narrow, mandatoryTopics: true);
368406
await enterTopic(tester, narrow: narrow, topic: 'new topic');
369407
await tester.pump();
370408
checkComposeBoxHintTexts(tester,

0 commit comments

Comments
 (0)