Skip to content

Commit 9067f7a

Browse files
committed
msglist: Only show stream name in AllMessagesNarrow
First in a series of changes to update stream/topic recipient headers. Stream name is now only displayed in `AllMessagesNarrow` and clicking this name navigates to the stream. Renamed `StreamTopicRecipientHeader` to `StreamMessageRecipientHeader` to reflect this conceptual change. Also adjusted existing test for unknown streams to explicitly use `AllMessagesNarrow`. Added doc links to design specs and upcoming commits will incrementally bring these headers closer to the desired design.
1 parent 82eb41e commit 9067f7a

File tree

2 files changed

+91
-24
lines changed

2 files changed

+91
-24
lines changed

lib/widgets/message_list.dart

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,14 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
341341
padding: EdgeInsets.symmetric(vertical: 16.0),
342342
child: CircularProgressIndicator())); // TODO perhaps a different indicator
343343
case MessageListRecipientHeaderItem():
344-
final header = RecipientHeader(message: data.message);
344+
final header = RecipientHeader(message: data.message, narrow: widget.narrow);
345345
return StickyHeaderItem(allowOverflow: true,
346346
header: header, child: header);
347347
case MessageListMessageItem():
348+
final header = RecipientHeader(message: data.message, narrow: widget.narrow);
348349
return MessageItem(
349350
key: ValueKey(data.message.id),
351+
header: header,
350352
trailingWhitespace: i == 1 ? 8 : 11,
351353
item: data);
352354
}
@@ -445,16 +447,17 @@ class MarkAsReadWidget extends StatelessWidget {
445447
}
446448

447449
class RecipientHeader extends StatelessWidget {
448-
const RecipientHeader({super.key, required this.message});
450+
const RecipientHeader({super.key, required this.message, required this.narrow});
449451

450452
final Message message;
453+
final Narrow narrow;
451454

452455
@override
453456
Widget build(BuildContext context) {
454-
// TODO recipient headings depend on narrow
455457
final message = this.message;
456458
return switch (message) {
457-
StreamMessage() => StreamTopicRecipientHeader(message: message),
459+
StreamMessage() => StreamMessageRecipientHeader(message: message,
460+
showStream: narrow is AllMessagesNarrow),
458461
DmMessage() => DmRecipientHeader(message: message),
459462
};
460463
}
@@ -464,18 +467,20 @@ class MessageItem extends StatelessWidget {
464467
const MessageItem({
465468
super.key,
466469
required this.item,
470+
required this.header,
467471
this.trailingWhitespace,
468472
});
469473

470474
final MessageListMessageItem item;
475+
final Widget header;
471476
final double? trailingWhitespace;
472477

473478
@override
474479
Widget build(BuildContext context) {
475480
final message = item.message;
476481
return StickyHeaderItem(
477482
allowOverflow: !item.isLastInBlock,
478-
header: RecipientHeader(message: message),
483+
header: header,
479484
child: _UnreadMarker(
480485
isRead: message.flags.contains(MessageFlag.read),
481486
child: ColoredBox(
@@ -528,17 +533,23 @@ class _UnreadMarker extends StatelessWidget {
528533
}
529534
}
530535

531-
class StreamTopicRecipientHeader extends StatelessWidget {
532-
const StreamTopicRecipientHeader({super.key, required this.message});
536+
class StreamMessageRecipientHeader extends StatelessWidget {
537+
const StreamMessageRecipientHeader({
538+
super.key,
539+
required this.message,
540+
required this.showStream,
541+
});
533542

534543
final StreamMessage message;
544+
final bool showStream;
535545

536546
@override
537547
Widget build(BuildContext context) {
548+
// For design specs, see:
549+
// https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=538%3A20849&mode=dev
550+
// https://github.com/zulip/zulip-mobile/issues/5511
538551
final store = PerAccountStoreWidget.of(context);
539552

540-
final stream = store.streams[message.streamId];
541-
final streamName = stream?.name ?? message.displayRecipient; // TODO(log) if missing
542553
final topic = message.subject;
543554

544555
final subscription = store.subscriptions[message.streamId];
@@ -548,6 +559,24 @@ class StreamTopicRecipientHeader extends StatelessWidget {
548559
? Colors.white
549560
: Colors.black;
550561

562+
final Widget streamWidget;
563+
if (!showStream) {
564+
streamWidget = const SizedBox.shrink();
565+
} else {
566+
final stream = store.streams[message.streamId];
567+
final streamName = stream?.name ?? message.displayRecipient; // TODO(log) if missing
568+
569+
streamWidget = GestureDetector(
570+
onTap: () => Navigator.push(context,
571+
MessageListPage.buildRoute(context: context,
572+
narrow: StreamNarrow(message.streamId))),
573+
child: RecipientHeaderChevronContainer(
574+
color: streamColor,
575+
// TODO globe/lock icons for web-public and private streams
576+
child: Text(streamName,
577+
style: TextStyle(color: contrastingColor))));
578+
}
579+
551580
return GestureDetector(
552581
onTap: () => Navigator.push(context,
553582
MessageListPage.buildRoute(context: context,
@@ -556,14 +585,7 @@ class StreamTopicRecipientHeader extends StatelessWidget {
556585
color: _kStreamMessageBorderColor,
557586
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [
558587
// TODO(#282): Long stream name will break layout; find a fix.
559-
GestureDetector(
560-
onTap: () => Navigator.push(context,
561-
MessageListPage.buildRoute(context: context,
562-
narrow: StreamNarrow(message.streamId))),
563-
child: RecipientHeaderChevronContainer(
564-
color: streamColor,
565-
// TODO globe/lock icons for web-public and private streams
566-
child: Text(streamName, style: TextStyle(color: contrastingColor)))),
588+
streamWidget,
567589
Expanded(
568590
child: Padding(
569591
// Web has padding 9, 3, 3, 2 here; but 5px is the chevron.

test/widgets/message_list_test.dart

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ void main() {
4040
bool foundOldest = true,
4141
int? messageCount,
4242
List<Message>? messages,
43+
List<ZulipStream>? streams,
4344
UnreadMessagesSnapshot? unreadMsgs,
4445
}) async {
4546
addTearDown(testBinding.reset);
46-
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot(unreadMsgs: unreadMsgs));
47+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot(
48+
streams: streams, unreadMsgs: unreadMsgs));
4749
store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
4850
connection = store.connection as FakeApiConnection;
4951

@@ -194,22 +196,65 @@ void main() {
194196
});
195197

196198
group('recipient headers', () {
199+
group('StreamMessageRecipientHeader', () {
200+
final stream = eg.stream(name: 'stream name');
201+
final message = eg.streamMessage(stream: stream, topic: 'topic name');
202+
203+
FinderResult findInMessageList(String text) {
204+
// Stream name shows up in [AppBar] so need to avoid matching that
205+
return find.descendant(
206+
of: find.byType(MessageList),
207+
matching: find.text(text)).evaluate();
208+
}
209+
210+
testWidgets('show stream name in AllMessagesNarrow', (tester) async {
211+
await setupMessageListPage(tester,
212+
narrow: const AllMessagesNarrow(),
213+
messages: [message], streams: [stream]);
214+
await tester.pump();
215+
check(findInMessageList('stream name')).length.equals(1);
216+
check(findInMessageList('topic name')).length.equals(1);
217+
});
218+
219+
testWidgets('do not show stream name in StreamNarrow', (tester) async {
220+
await setupMessageListPage(tester,
221+
narrow: StreamNarrow(stream.streamId),
222+
messages: [message], streams: [stream]);
223+
await tester.pump();
224+
check(findInMessageList('stream name')).length.equals(0);
225+
check(findInMessageList('topic name')).length.equals(1);
226+
});
227+
228+
testWidgets('do not show stream name in TopicNarrow', (tester) async {
229+
await setupMessageListPage(tester,
230+
narrow: TopicNarrow.ofMessage(message),
231+
messages: [message], streams: [stream]);
232+
await tester.pump();
233+
check(findInMessageList('stream name')).length.equals(0);
234+
check(findInMessageList('topic name')).length.equals(1);
235+
});
236+
});
237+
197238
testWidgets('show stream name from message when stream unknown', (tester) async {
198239
// This can perfectly well happen, because message fetches can race
199240
// with events.
200241
final stream = eg.stream(name: 'stream name');
201-
await setupMessageListPage(tester, messages: [
202-
eg.streamMessage(stream: stream),
203-
]);
242+
await setupMessageListPage(tester,
243+
narrow: const AllMessagesNarrow(),
244+
messages: [
245+
eg.streamMessage(stream: stream),
246+
]);
204247
await tester.pump();
205248
tester.widget(find.text('stream name'));
206249
});
207250

208251
testWidgets('show stream name from stream data when known', (tester) async {
209252
final stream = eg.stream(name: 'old stream name');
210-
await setupMessageListPage(tester, messages: [
211-
eg.streamMessage(stream: stream),
212-
]);
253+
await setupMessageListPage(tester,
254+
narrow: const AllMessagesNarrow(),
255+
messages: [
256+
eg.streamMessage(stream: stream),
257+
]);
213258
// TODO(#182) this test would be more realistic using a StreamUpdateEvent
214259
store.handleEvent(StreamCreateEvent(id: stream.streamId, streams: [
215260
ZulipStream.fromJson({

0 commit comments

Comments
 (0)