Skip to content

Commit 24b5baf

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 #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 78cf9e5 commit 24b5baf

File tree

4 files changed

+59
-3
lines changed

4 files changed

+59
-3
lines changed

lib/widgets/icons.dart

+18
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

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

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

lib/widgets/theme.dart

+7
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

+16
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)