Skip to content

Commit eb67f92

Browse files
committed
msglist: Show topic visibility on app bar
The design took some inspiration from the legacy mobile app. This displays a privacy level related icon (e.g.: web public, invite only). We will have a different place to show channel mute/unmute status in zulip#347. The color for the icon is taken from the web app: https://github.com/zulip/zulip/blob/dc58c8450f8524f226115a7b449b05e01ae15d8b/web/styles/message_header.css#L296-L297 https://github.com/zulip/zulip/blob/dc58c8450f8524f226115a7b449b05e01ae15d8b/web/styles/app_variables.css#L590 https://github.com/zulip/zulip/blob/dc58c8450f8524f226115a7b449b05e01ae15d8b/web/styles/app_variables.css#L1330 In the web app, these colors are used for the topic visibility icons on message recipient headers. To maintain the different conventional title alignment on iOS and Android, we borrow some checks from AppBar in title centering. Signed-off-by: Zixuan James Li <[email protected]>
1 parent 169af8f commit eb67f92

File tree

4 files changed

+59
-3
lines changed

4 files changed

+59
-3
lines changed

lib/widgets/icons.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,21 @@ IconData iconDataForStream(ZulipStream stream) {
139139
ZulipStream() => ZulipIcons.hash_sign,
140140
};
141141
}
142+
143+
IconData? iconDataForTopicVisibilityPolicy(UserTopicVisibilityPolicy policy) {
144+
switch (policy) {
145+
case UserTopicVisibilityPolicy.muted:
146+
return ZulipIcons.mute;
147+
case UserTopicVisibilityPolicy.unmuted:
148+
return ZulipIcons.unmute;
149+
case UserTopicVisibilityPolicy.followed:
150+
return ZulipIcons.follow;
151+
case UserTopicVisibilityPolicy.none:
152+
return null;
153+
case UserTopicVisibilityPolicy.unknown:
154+
// This case is unreachable (or should be) because we keep `unknown` out
155+
// of our data structures. We plan to remove the `unknown` case in #1074.
156+
assert(false);
157+
return null;
158+
}
159+
}

lib/widgets/message_list.dart

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,24 @@ class MessageListAppBarTitle extends StatelessWidget {
337337
required ZulipStream? stream,
338338
required String topic,
339339
}) {
340-
return Text(topic, style: const TextStyle(
341-
fontSize: 13,
342-
).merge(weightVariableTextStyle(context)));
340+
final store = PerAccountStoreWidget.of(context);
341+
final designVariables = DesignVariables.of(context);
342+
final icon = (stream == null) ? null
343+
: iconDataForTopicVisibilityPolicy(
344+
store.topicVisibilityPolicy(stream.streamId, topic));
345+
return Row(
346+
mainAxisSize: MainAxisSize.min,
347+
children: [
348+
Flexible(child: Text(topic, style: const TextStyle(
349+
fontSize: 13,
350+
).merge(weightVariableTextStyle(context)))),
351+
if (icon != null)
352+
Padding(
353+
padding: const EdgeInsetsDirectional.only(start: 4),
354+
child: Icon(icon,
355+
// TODO(design) copies the recipient header in web; is there a better color?
356+
color: designVariables.colorMessageHeaderIconInteractive, size: 14)),
357+
]);
343358
}
344359

345360
// TODO(upstream): provide an API for this

lib/widgets/theme.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
136136
title: const Color(0xff1a1a1a),
137137
channelColorSwatches: ChannelColorSwatches.light,
138138
atMentionMarker: const HSLColor.fromAHSL(0.5, 0, 0, 0.2).toColor(),
139+
colorMessageHeaderIconInteractive: Colors.black.withValues(alpha: 0.2),
139140
contextMenuCancelBg: const Color(0xff797986).withValues(alpha: 0.15),
140141
contextMenuCancelPressedBg: const Color(0xff797986).withValues(alpha: 0.20),
141142
dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.35, 0.93).toColor(),
@@ -180,6 +181,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
180181
contextMenuCancelPressedBg: const Color(0xff797986).withValues(alpha: 0.20), // the same as the light mode in Figma
181182
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
182183
atMentionMarker: const HSLColor.fromAHSL(0.4, 0, 0, 1).toColor(),
184+
colorMessageHeaderIconInteractive: Colors.white.withValues(alpha: 0.2),
183185
dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(),
184186
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
185187
groupDmConversationIcon: Colors.white.withValues(alpha: 0.5),
@@ -225,6 +227,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
225227
required this.title,
226228
required this.channelColorSwatches,
227229
required this.atMentionMarker,
230+
required this.colorMessageHeaderIconInteractive,
228231
required this.contextMenuCancelBg,
229232
required this.contextMenuCancelPressedBg,
230233
required this.dmHeaderBg,
@@ -278,6 +281,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
278281

279282
// Not named variables in Figma; taken from older Figma drafts, or elsewhere.
280283
final Color atMentionMarker;
284+
final Color colorMessageHeaderIconInteractive;
281285
final Color contextMenuCancelBg; // In Figma, but unnamed.
282286
final Color contextMenuCancelPressedBg; // In Figma, but unnamed.
283287
final Color dmHeaderBg;
@@ -318,6 +322,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
318322
Color? title,
319323
ChannelColorSwatches? channelColorSwatches,
320324
Color? atMentionMarker,
325+
Color? colorMessageHeaderIconInteractive,
321326
Color? contextMenuCancelBg,
322327
Color? contextMenuCancelPressedBg,
323328
Color? dmHeaderBg,
@@ -357,6 +362,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
357362
title: title ?? this.title,
358363
channelColorSwatches: channelColorSwatches ?? this.channelColorSwatches,
359364
atMentionMarker: atMentionMarker ?? this.atMentionMarker,
365+
colorMessageHeaderIconInteractive: colorMessageHeaderIconInteractive ?? this.colorMessageHeaderIconInteractive,
360366
contextMenuCancelBg: contextMenuCancelBg ?? this.contextMenuCancelBg,
361367
contextMenuCancelPressedBg: contextMenuCancelPressedBg ?? this.contextMenuCancelPressedBg,
362368
dmHeaderBg: dmHeaderBg ?? this.dmHeaderBg,
@@ -403,6 +409,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
403409
title: Color.lerp(title, other.title, t)!,
404410
channelColorSwatches: ChannelColorSwatches.lerp(channelColorSwatches, other.channelColorSwatches, t),
405411
atMentionMarker: Color.lerp(atMentionMarker, other.atMentionMarker, t)!,
412+
colorMessageHeaderIconInteractive: Color.lerp(colorMessageHeaderIconInteractive, other.colorMessageHeaderIconInteractive, t)!,
406413
contextMenuCancelBg: Color.lerp(contextMenuCancelBg, other.contextMenuCancelBg, t)!,
407414
contextMenuCancelPressedBg: Color.lerp(contextMenuCancelPressedBg, other.contextMenuCancelPressedBg, t)!,
408415
dmHeaderBg: Color.lerp(dmHeaderBg, other.dmHeaderBg, t)!,

test/widgets/message_list_test.dart

Lines changed: 16 additions & 0 deletions
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', () {

0 commit comments

Comments
 (0)