Skip to content

Commit 8942165

Browse files
committed
msglist: Show channel name and topic name on two rows
This mostly follows the legacy mobile app. Notably, this changes the title text to use normal font weight (wght=400), and breaks the text into two rows for topic narrow. This will eventually be superseded by #1039, so we should keep the implementation as simple as possible for now. There are some differences from the old design: The legacy mobile uses different colors for the title text, depending on the color of the channel, to make the text more visible. We currently don't have that, so the text just uses the ambient color. The original design also displays the 'mute' icon when the channel is muted: https://github.com/zulip/zulip-mobile/blob/a115df1f71c9dc31e9b41060a8d57b51c017d786/src/streams/StreamIcon.js#L20-L29 In the Flutter app, however, only the privacy level related icons are displayed (e.g.: web public, invite only). We continue to leave out the 'mute' icon in this implementation. This can change after we have a concrete redesign plan. This implementation also shows the corresponding icons for 'muted' and 'unmuted' topics; previously, only the icon for 'follow' was displayed. And we continue using the existing icons in the Flutter app, without trying to match with the exact ones in the old design. References: https://github.com/zulip/zulip-mobile/blob/a115df1f71c9dc31e9b41060a8d57b51c017d786/src/title/TitleStream.js#L113-L141 https://github.com/zulip/zulip-mobile/blob/a115df1f71c9dc31e9b41060a8d57b51c017d786/src/styles/navStyles.js#L5-L18 Fixes: #348 Signed-off-by: Zixuan James Li <[email protected]>
1 parent b95b9d2 commit 8942165

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

lib/widgets/icons.dart

+17
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,20 @@ IconData iconDataForStream(ZulipStream stream) {
127127
ZulipStream() => ZulipIcons.hash_sign,
128128
};
129129
}
130+
131+
IconData? iconDataForTopic(UserTopicVisibilityPolicy policy) {
132+
switch (policy) {
133+
case UserTopicVisibilityPolicy.muted:
134+
return ZulipIcons.mute;
135+
case UserTopicVisibilityPolicy.unmuted:
136+
return ZulipIcons.unmute;
137+
case UserTopicVisibilityPolicy.followed:
138+
return ZulipIcons.follow;
139+
case UserTopicVisibilityPolicy.none:
140+
return null;
141+
case UserTopicVisibilityPolicy.unknown:
142+
// TODO(#1078) remove this
143+
assert(false);
144+
return null;
145+
}
146+
}

lib/widgets/message_list.dart

+37-12
Original file line numberDiff line numberDiff line change
@@ -313,20 +313,42 @@ class MessageListAppBarTitle extends StatelessWidget {
313313

314314
Widget _buildStreamRow(BuildContext context, {
315315
ZulipStream? stream,
316-
required String text,
317316
}) {
318-
// A null [Icon.icon] makes a blank space.
319-
final icon = (stream != null) ? iconDataForStream(stream) : null;
320-
return Row(
317+
final icon = (stream == null) ? ZulipIcons.hash_sign : iconDataForStream(stream);
318+
Widget result = Row(
321319
mainAxisSize: MainAxisSize.min,
322320
// TODO(design): The vertical alignment of the stream privacy icon is a bit ad hoc.
323321
// For screenshots of some experiments, see:
324322
// https://github.com/zulip/zulip-flutter/pull/219#discussion_r1281024746
325323
crossAxisAlignment: CrossAxisAlignment.center,
326324
children: [
327-
Icon(size: 16, icon),
328-
const SizedBox(width: 4),
329-
Flexible(child: Text(text)),
325+
Padding(padding: const EdgeInsetsDirectional.only(end: 8.0),
326+
child: Icon(size: 20, icon)),
327+
Flexible(child: Text(stream?.name ?? '(unknown stream)',
328+
style: const TextStyle(
329+
fontSize: 20,
330+
).merge(weightVariableTextStyle(context)))),
331+
]);
332+
333+
return result;
334+
}
335+
336+
Widget _buildTopicRow(BuildContext context, {
337+
required ZulipStream? stream,
338+
required String topic,
339+
}) {
340+
final store = PerAccountStoreWidget.of(context);
341+
final icon = (stream == null) ? null
342+
: iconDataForTopic(store.topicVisibilityPolicy(stream.streamId, topic));
343+
return Row(
344+
children: [
345+
Flexible(child: Text(topic, style: const TextStyle(
346+
fontSize: 13,
347+
).merge(weightVariableTextStyle(context)))),
348+
if (icon != null)
349+
Padding(
350+
padding: const EdgeInsetsDirectional.only(start: 4),
351+
child: Opacity(opacity: 0.4, child: Icon(icon, size: 14))),
330352
]);
331353
}
332354

@@ -347,21 +369,24 @@ class MessageListAppBarTitle extends StatelessWidget {
347369
case ChannelNarrow(:var streamId):
348370
final store = PerAccountStoreWidget.of(context);
349371
final stream = store.streams[streamId];
350-
final streamName = stream?.name ?? '(unknown channel)';
351-
return _buildStreamRow(context, stream: stream, text: streamName);
372+
return _buildStreamRow(context, stream: stream);
352373

353374
case TopicNarrow(:var streamId, :var topic):
354375
final store = PerAccountStoreWidget.of(context);
355376
final stream = store.streams[streamId];
356-
final streamName = stream?.name ?? '(unknown channel)';
377+
357378
return SizedBox(
358379
width: double.infinity,
359380
child: GestureDetector(
360381
behavior: HitTestBehavior.translucent,
361382
onLongPress: () => showTopicActionSheet(context,
362383
channelId: streamId, topic: topic),
363-
child: _buildStreamRow(
364-
context, stream: stream, text: "$streamName > $topic")));
384+
child: Column(
385+
crossAxisAlignment: CrossAxisAlignment.start,
386+
children: [
387+
_buildStreamRow(context, stream: stream),
388+
_buildTopicRow(context, stream: stream, topic: topic),
389+
])));
365390

366391
case DmNarrow(:var otherRecipientIds):
367392
final store = PerAccountStoreWidget.of(context);

test/widgets/message_list_test.dart

+17-1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ void main() {
152152
.page.isA<MessageListPage>().initNarrow
153153
.equals(ChannelNarrow(channel.streamId));
154154
});
155+
156+
testWidgets('show topic visibility policy for topic narrows', (tester) async {
157+
final channel = eg.stream();
158+
const topic = 'topic';
159+
await setupMessageListPage(tester,
160+
narrow: TopicNarrow(channel.streamId, topic),
161+
streams: [channel], subscriptions: [eg.subscription(channel)],
162+
messageCount: 1);
163+
await store.handleEvent(eg.userTopicEvent(
164+
channel.streamId, topic, UserTopicVisibilityPolicy.muted));
165+
await tester.pump();
166+
167+
check(find.descendant(
168+
of: find.byType(MessageListAppBarTitle),
169+
matching: find.byIcon(ZulipIcons.mute))).findsOne();
170+
});
155171
});
156172

157173
group('presents message content appropriately', () {
@@ -725,7 +741,7 @@ void main() {
725741
).length.equals(1);
726742
check(find.descendant(
727743
of: find.byType(MessageListAppBarTitle),
728-
matching: find.text('${channel.name} > new topic')).evaluate()
744+
matching: find.text('new topic')).evaluate()
729745
).length.equals(1);
730746
});
731747
});

0 commit comments

Comments
 (0)