Skip to content

Commit fa85b9d

Browse files
committed
compose: Show special hint text for inputs if empty topic
1 parent 7bdef99 commit fa85b9d

File tree

2 files changed

+56
-8
lines changed

2 files changed

+56
-8
lines changed

lib/widgets/compose_box.dart

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,17 @@ class _ContentInputState extends State<_ContentInput> with WidgetsBindingObserve
429429
@override
430430
Widget build(BuildContext context) {
431431
final designVariables = DesignVariables.of(context);
432+
TextStyle hintStyle = TextStyle(
433+
color: designVariables.textInput.withFadedAlpha(0.5));
434+
435+
if (widget.destination.destination
436+
// ignore: constant_pattern_never_matches_value_type // null topic names soon to be enabled
437+
case StreamDestination(topic: TopicName(displayName: null))) {
438+
// TODO(#1285): This applies to the entire hint text; ideally we'd only
439+
// want to italize the "general chat" text, but this requires
440+
// special l10n support for the hint text string.
441+
hintStyle = hintStyle.copyWith(fontStyle: FontStyle.italic);
442+
}
432443

433444
return ComposeAutocomplete(
434445
narrow: widget.narrow,
@@ -470,8 +481,7 @@ class _ContentInputState extends State<_ContentInput> with WidgetsBindingObserve
470481
// this and offering two lines of touchable area.
471482
contentPadding: const EdgeInsets.symmetric(vertical: _verticalPadding),
472483
hintText: widget.hintText,
473-
hintStyle: TextStyle(
474-
color: designVariables.textInput.withFadedAlpha(0.5))))))));
484+
hintStyle: hintStyle))))));
475485
}
476486
}
477487

@@ -529,7 +539,8 @@ class _StreamContentInputState extends State<_StreamContentInput> {
529539
destination: TopicNarrow(widget.narrow.streamId, topic),
530540
controller: widget.controller,
531541
hintText: zulipLocalizations.composeBoxChannelContentHint(
532-
streamName, topic.displayName));
542+
// ignore: dead_null_aware_expression // null topic names soon to be enabled
543+
streamName, topic.displayName ?? store.realmEmptyTopicDisplayName));
533544
}
534545
}
535546

@@ -548,6 +559,9 @@ class _TopicInput extends StatelessWidget {
548559
height: 22 / 20,
549560
color: designVariables.textInput.withFadedAlpha(0.9),
550561
).merge(weightVariableTextStyle(context, wght: 600));
562+
final store = PerAccountStoreWidget.of(context);
563+
final allowsEmptyTopics =
564+
store.connection.zulipFeatureLevel! >= 334 && !store.realmMandatoryTopics;
551565

552566
return TopicAutocomplete(
553567
streamId: streamId,
@@ -565,8 +579,11 @@ class _TopicInput extends StatelessWidget {
565579
textInputAction: TextInputAction.next,
566580
style: topicTextStyle,
567581
decoration: InputDecoration(
568-
hintText: zulipLocalizations.composeBoxTopicHintText,
582+
hintText: (allowsEmptyTopics)
583+
? store.realmEmptyTopicDisplayName
584+
: zulipLocalizations.composeBoxTopicHintText,
569585
hintStyle: topicTextStyle.copyWith(
586+
fontStyle: (allowsEmptyTopics) ? FontStyle.italic : null,
570587
color: designVariables.textInput.withFadedAlpha(0.5))))));
571588
}
572589
}
@@ -588,7 +605,8 @@ class _FixedDestinationContentInput extends StatelessWidget {
588605
final streamName = store.streams[streamId]?.name
589606
?? zulipLocalizations.composeBoxUnknownChannelName;
590607
return zulipLocalizations.composeBoxChannelContentHint(
591-
streamName, topic.displayName);
608+
// ignore: dead_null_aware_expression // null topic names soon to be enabled
609+
streamName, topic.displayName ?? store.realmEmptyTopicDisplayName);
592610

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

test/widgets/compose_box_test.dart

Lines changed: 33 additions & 3 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

@@ -317,11 +320,15 @@ void main() {
317320

318321
Future<void> prepare(WidgetTester tester, {
319322
required Narrow narrow,
323+
bool? mandatoryTopics,
324+
int? zulipFeatureLevel,
320325
}) async {
321326
await prepareComposeBox(tester,
322327
narrow: narrow,
323328
otherUsers: [eg.otherUser, eg.thirdUser],
324-
streams: [channel]);
329+
streams: [channel],
330+
mandatoryTopics: mandatoryTopics,
331+
zulipFeatureLevel: zulipFeatureLevel);
325332
}
326333

327334
void checkComposeBoxHintTexts(WidgetTester tester, {
@@ -340,6 +347,22 @@ void main() {
340347

341348
testWidgets('to ChannelNarrow without topic', (tester) async {
342349
await prepare(tester, narrow: ChannelNarrow(channel.streamId));
350+
checkComposeBoxHintTexts(tester,
351+
topicHintText: eg.defaultRealmEmptyTopicDisplayName,
352+
contentHintText: 'Message #${channel.name} > ${eg.defaultRealmEmptyTopicDisplayName}');
353+
}, skip: true); // null topic names soon to be enabled
354+
355+
testWidgets('to ChannelNarrow without topic; mandatory topics', (tester) async {
356+
await prepare(tester, narrow: ChannelNarrow(channel.streamId),
357+
mandatoryTopics: true);
358+
checkComposeBoxHintTexts(tester,
359+
topicHintText: 'Topic',
360+
contentHintText: 'Message #${channel.name} > ${eg.defaultRealmEmptyTopicDisplayName}');
361+
}, skip: true); // null topic names soon to be enabled
362+
363+
testWidgets('legacy: to ChannelNarrow without topic', (tester) async {
364+
await prepare(tester, narrow: ChannelNarrow(channel.streamId),
365+
zulipFeatureLevel: 333);
343366
checkComposeBoxHintTexts(tester,
344367
topicHintText: 'Topic',
345368
contentHintText: 'Message #${channel.name} > (no topic)');
@@ -351,7 +374,7 @@ void main() {
351374
await enterTopic(tester, narrow: narrow, topic: 'new topic');
352375
await tester.pump();
353376
checkComposeBoxHintTexts(tester,
354-
topicHintText: 'Topic',
377+
topicHintText: eg.defaultRealmEmptyTopicDisplayName,
355378
contentHintText: 'Message #${channel.name} > new topic');
356379
});
357380

@@ -362,6 +385,13 @@ void main() {
362385
contentHintText: 'Message #${channel.name} > topic');
363386
});
364387

388+
testWidgets('to TopicNarrow with empty topic', (tester) async {
389+
await prepare(tester,
390+
narrow: TopicNarrow(channel.streamId, TopicName('')));
391+
checkComposeBoxHintTexts(tester, contentHintText:
392+
'Message #${channel.name} > ${eg.defaultRealmEmptyTopicDisplayName}');
393+
}, skip: true); // null topic names soon to be enabled
394+
365395
testWidgets('to DmNarrow with self', (tester) async {
366396
await prepare(tester, narrow: DmNarrow.withUser(
367397
eg.selfUser.userId, selfUserId: eg.selfUser.userId));

0 commit comments

Comments
 (0)