Skip to content

Commit 999071e

Browse files
committed
compose: Show special hint text for inputs if empty topic
Signed-off-by: Zixuan James Li <[email protected]>
1 parent 5e50fa6 commit 999071e

File tree

2 files changed

+61
-15
lines changed

2 files changed

+61
-15
lines changed

lib/widgets/compose_box.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,8 @@ class _StreamContentInputState extends State<_StreamContentInput> {
587587
destination: TopicNarrow(widget.narrow.streamId, topic),
588588
controller: widget.controller,
589589
hintText: zulipLocalizations.composeBoxChannelContentHint(
590-
'#$streamName > ${topic.displayName}'));
590+
// ignore: dead_null_aware_expression // null topic names soon to be enabled
591+
'#$streamName > ${topic.displayName ?? store.realmEmptyTopicDisplayName}'));
591592
}
592593
}
593594

@@ -606,6 +607,9 @@ class _TopicInput extends StatelessWidget {
606607
height: 22 / 20,
607608
color: designVariables.textInput.withFadedAlpha(0.9),
608609
).merge(weightVariableTextStyle(context, wght: 600));
610+
final store = PerAccountStoreWidget.of(context);
611+
final allowsEmptyTopics =
612+
store.connection.zulipFeatureLevel! >= 334 && !store.realmMandatoryTopics;
609613

610614
return TopicAutocomplete(
611615
streamId: streamId,
@@ -623,8 +627,11 @@ class _TopicInput extends StatelessWidget {
623627
textInputAction: TextInputAction.next,
624628
style: topicTextStyle,
625629
decoration: InputDecoration(
626-
hintText: zulipLocalizations.composeBoxTopicHintText,
630+
hintText: allowsEmptyTopics
631+
? store.realmEmptyTopicDisplayName
632+
: zulipLocalizations.composeBoxTopicHintText,
627633
hintStyle: topicTextStyle.copyWith(
634+
fontStyle: allowsEmptyTopics ? FontStyle.italic : null,
628635
color: designVariables.textInput.withFadedAlpha(0.5))))));
629636
}
630637
}
@@ -646,7 +653,8 @@ class _FixedDestinationContentInput extends StatelessWidget {
646653
final streamName = store.streams[streamId]?.name
647654
?? zulipLocalizations.unknownChannelName;
648655
return zulipLocalizations.composeBoxChannelContentHint(
649-
'#$streamName > ${topic.displayName}');
656+
// ignore: dead_null_aware_expression // null topic names soon to be enabled
657+
'#$streamName > ${topic.displayName ?? store.realmEmptyTopicDisplayName}');
650658

651659
case DmNarrow(otherRecipientIds: []): // The self-1:1 thread.
652660
return zulipLocalizations.composeBoxSelfDmContentHint;

test/widgets/compose_box_test.dart

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,18 @@ void main() {
4747
List<User> otherUsers = const [],
4848
List<ZulipStream> streams = const [],
4949
bool? mandatoryTopics,
50+
int? zulipFeatureLevel,
5051
}) async {
5152
if (narrow case ChannelNarrow(:var streamId) || TopicNarrow(: var streamId)) {
5253
assert(streams.any((stream) => stream.streamId == streamId),
5354
'Add a channel with "streamId" the same as of $narrow.streamId to the store.');
5455
}
5556
addTearDown(testBinding.reset);
5657
selfUser ??= eg.selfUser;
57-
final selfAccount = eg.account(user: selfUser);
58+
zulipFeatureLevel ??= eg.futureZulipFeatureLevel;
59+
final selfAccount = eg.account(user: selfUser, zulipFeatureLevel: zulipFeatureLevel);
5860
await testBinding.globalStore.add(selfAccount, eg.initialSnapshot(
61+
zulipFeatureLevel: zulipFeatureLevel,
5962
realmMandatoryTopics: mandatoryTopics,
6063
));
6164

@@ -326,11 +329,15 @@ void main() {
326329

327330
Future<void> prepare(WidgetTester tester, {
328331
required Narrow narrow,
332+
bool? mandatoryTopics,
333+
int? zulipFeatureLevel,
329334
}) async {
330335
await prepareComposeBox(tester,
331336
narrow: narrow,
332337
otherUsers: [eg.otherUser, eg.thirdUser],
333-
streams: [channel]);
338+
streams: [channel],
339+
mandatoryTopics: mandatoryTopics,
340+
zulipFeatureLevel: zulipFeatureLevel);
334341
}
335342

336343
void checkComposeBoxHintTexts(WidgetTester tester, {
@@ -347,48 +354,79 @@ void main() {
347354
of: contentInputFinder, matching: find.text(contentHintText))).findsOne();
348355
}
349356

350-
testWidgets('to ChannelNarrow without topic', (tester) async {
351-
await prepare(tester, narrow: ChannelNarrow(channel.streamId));
357+
testWidgets('to ChannelNarrow with empty topic', (tester) async {
358+
await prepare(tester, narrow: ChannelNarrow(channel.streamId),
359+
mandatoryTopics: false);
360+
checkComposeBoxHintTexts(tester,
361+
topicHintText: eg.defaultRealmEmptyTopicDisplayName,
362+
contentHintText: 'Message #${channel.name} > ${eg.defaultRealmEmptyTopicDisplayName}');
363+
}, skip: true); // null topic names soon to be enabled
364+
365+
testWidgets('to ChannelNarrow with empty topic (mandatory topics)', (tester) async {
366+
await prepare(tester, narrow: ChannelNarrow(channel.streamId),
367+
mandatoryTopics: true);
368+
checkComposeBoxHintTexts(tester,
369+
topicHintText: 'Topic',
370+
contentHintText: 'Message #${channel.name} > ${eg.defaultRealmEmptyTopicDisplayName}');
371+
}, skip: true); // null topic names soon to be enabled
372+
373+
testWidgets('legacy: to ChannelNarrow with empty topic', (tester) async {
374+
await prepare(tester, narrow: ChannelNarrow(channel.streamId),
375+
mandatoryTopics: false,
376+
zulipFeatureLevel: 333);
352377
checkComposeBoxHintTexts(tester,
353378
topicHintText: 'Topic',
354379
contentHintText: 'Message #${channel.name} > (no topic)');
355380
});
356381

357-
testWidgets('to ChannelNarrow with topic', (tester) async {
382+
testWidgets('to ChannelNarrow with non-empty topic', (tester) async {
358383
final narrow = ChannelNarrow(channel.streamId);
359-
await prepare(tester, narrow: narrow);
384+
await prepare(tester, narrow: narrow,
385+
mandatoryTopics: false);
360386
await enterTopic(tester, narrow: narrow, topic: 'new topic');
361387
await tester.pump();
362388
checkComposeBoxHintTexts(tester,
363-
topicHintText: 'Topic',
389+
topicHintText: eg.defaultRealmEmptyTopicDisplayName,
364390
contentHintText: 'Message #${channel.name} > new topic');
365391
});
366392

367-
testWidgets('to TopicNarrow', (tester) async {
393+
testWidgets('to TopicNarrow with non-empty topic', (tester) async {
368394
await prepare(tester,
369-
narrow: TopicNarrow(channel.streamId, TopicName('topic')));
395+
narrow: TopicNarrow(channel.streamId, TopicName('topic')),
396+
mandatoryTopics: false);
370397
checkComposeBoxHintTexts(tester,
371398
contentHintText: 'Message #${channel.name} > topic');
372399
});
373400

401+
testWidgets('to TopicNarrow with empty topic', (tester) async {
402+
await prepare(tester,
403+
narrow: TopicNarrow(channel.streamId, TopicName('')),
404+
mandatoryTopics: false);
405+
checkComposeBoxHintTexts(tester, contentHintText:
406+
'Message #${channel.name} > ${eg.defaultRealmEmptyTopicDisplayName}');
407+
}, skip: true); // null topic names soon to be enabled
408+
374409
testWidgets('to DmNarrow with self', (tester) async {
375410
await prepare(tester, narrow: DmNarrow.withUser(
376-
eg.selfUser.userId, selfUserId: eg.selfUser.userId));
411+
eg.selfUser.userId, selfUserId: eg.selfUser.userId),
412+
mandatoryTopics: false);
377413
checkComposeBoxHintTexts(tester,
378414
contentHintText: 'Jot down something');
379415
});
380416

381417
testWidgets('to 1:1 DmNarrow', (tester) async {
382418
await prepare(tester, narrow: DmNarrow.withUser(
383-
eg.otherUser.userId, selfUserId: eg.selfUser.userId));
419+
eg.otherUser.userId, selfUserId: eg.selfUser.userId),
420+
mandatoryTopics: false);
384421
checkComposeBoxHintTexts(tester,
385422
contentHintText: 'Message @${eg.otherUser.fullName}');
386423
});
387424

388425
testWidgets('to group DmNarrow', (tester) async {
389426
await prepare(tester, narrow: DmNarrow.withOtherUsers(
390427
[eg.otherUser.userId, eg.thirdUser.userId],
391-
selfUserId: eg.selfUser.userId));
428+
selfUserId: eg.selfUser.userId),
429+
mandatoryTopics: false);
392430
checkComposeBoxHintTexts(tester,
393431
contentHintText: 'Message group');
394432
});

0 commit comments

Comments
 (0)