Skip to content

Commit 8af5930

Browse files
committed
home: Show starred-message count in main menu, subject to user setting
Fixes-partly: #1088
1 parent 9380ac7 commit 8af5930

File tree

11 files changed

+134
-66
lines changed

11 files changed

+134
-66
lines changed
Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,29 @@ import 'store.dart';
44
import 'text.dart';
55
import 'theme.dart';
66

7-
/// A widget to display a given number of unreads in a conversation.
7+
/// A widget to display a given number (e.g. of unread messages or of users).
88
///
99
/// See Figma's "counter-menu" component, which this is based on:
1010
/// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=2037-186671&m=dev
1111
/// It looks like that component was created for the main menu,
1212
/// then adapted for various other contexts, like the Inbox page.
13-
/// See [UnreadCountBadgeStyle].
14-
///
15-
/// Currently this widget supports only the component's "kind=unread" variant,
16-
/// not "kind=quantity".
17-
// TODO support the "kind=quantity" variant, update dartdoc
18-
class UnreadCountBadge extends StatelessWidget {
19-
const UnreadCountBadge({
13+
/// See [CounterBadgeStyle] and [CounterBadgeKind] for the possible variants.
14+
class CounterBadge extends StatelessWidget {
15+
const CounterBadge({
2016
super.key,
21-
this.style = UnreadCountBadgeStyle.other,
17+
this.style = CounterBadgeStyle.other,
18+
required this.kind,
2219
required this.count,
2320
required this.channelIdForBackground,
24-
});
21+
}) : assert(!(kind == CounterBadgeKind.quantity && channelIdForBackground != null));
2522

26-
final UnreadCountBadgeStyle style;
23+
final CounterBadgeStyle style;
24+
final CounterBadgeKind kind;
2725
final int count;
2826

2927
/// An optional [Subscription.streamId], for a channel-colorized background.
3028
///
31-
/// Useful when this badge represents messages in one specific channel.
29+
/// Useful when this counter represents unreads in one specific channel.
3230
///
3331
/// If null, the default neutral background will be used.
3432
// TODO remove; the Figma doesn't use this anymore.
@@ -48,40 +46,54 @@ class UnreadCountBadge extends StatelessWidget {
4846
final swatch = colorSwatchFor(context, subscription);
4947
backgroundColor = swatch.unreadCountBadgeBackground;
5048
} else {
51-
textColor = designVariables.labelCounterUnread;
49+
textColor = switch (kind) {
50+
CounterBadgeKind.unread => designVariables.labelCounterUnread,
51+
CounterBadgeKind.quantity => designVariables.labelCounterQuantity,
52+
};
5253
backgroundColor = designVariables.bgCounterUnread;
5354
}
5455

5556
final padding = switch (style) {
56-
UnreadCountBadgeStyle.mainMenu =>
57+
CounterBadgeStyle.mainMenu =>
5758
const EdgeInsets.symmetric(horizontal: 5, vertical: 4),
58-
UnreadCountBadgeStyle.other =>
59+
CounterBadgeStyle.other =>
5960
const EdgeInsets.symmetric(horizontal: 5, vertical: 3),
6061
};
6162

62-
final double wght = switch (style) {
63-
UnreadCountBadgeStyle.mainMenu => 600,
64-
UnreadCountBadgeStyle.other => 500,
63+
final double wght = switch ((style, kind)) {
64+
(CounterBadgeStyle.mainMenu, CounterBadgeKind.unread ) => 600,
65+
(CounterBadgeStyle.mainMenu, CounterBadgeKind.quantity) => 500,
66+
(CounterBadgeStyle.other, CounterBadgeKind.unread ) => 500,
67+
(CounterBadgeStyle.other, CounterBadgeKind.quantity) => 500,
6568
};
6669

67-
return DecoratedBox(
68-
decoration: BoxDecoration(
69-
borderRadius: BorderRadius.circular(5),
70-
color: backgroundColor,
71-
),
72-
child: Padding(
73-
padding: padding,
74-
child: Text(
75-
style: TextStyle(
76-
fontSize: 16,
77-
height: (16 / 16),
78-
color: textColor,
79-
).merge(weightVariableTextStyle(context, wght: wght)),
80-
count.toString())));
70+
Widget result = Padding(
71+
padding: padding,
72+
child: Text(
73+
style: TextStyle(
74+
fontSize: 16,
75+
height: (16 / 16),
76+
color: textColor,
77+
).merge(weightVariableTextStyle(context, wght: wght)),
78+
count.toString()));
79+
80+
switch (kind) {
81+
case CounterBadgeKind.unread:
82+
result = DecoratedBox(
83+
decoration: BoxDecoration(
84+
borderRadius: BorderRadius.circular(5),
85+
color: backgroundColor,
86+
),
87+
child: result);
88+
case CounterBadgeKind.quantity:
89+
// no decoration
90+
}
91+
92+
return result;
8193
}
8294
}
8395

84-
enum UnreadCountBadgeStyle {
96+
enum CounterBadgeStyle {
8597
/// The style to use in the main menu.
8698
///
8799
/// Figma:
@@ -98,6 +110,22 @@ enum UnreadCountBadgeStyle {
98110
other,
99111
}
100112

113+
enum CounterBadgeKind {
114+
/// The counter counts unread messages.
115+
///
116+
/// Figma:
117+
/// Main-menu style: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=2037-185125&m=dev
118+
/// Other style: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=6205-26001&m=dev
119+
unread,
120+
121+
/// The counter counts something else, like users or starred messages.
122+
///
123+
/// Figma:
124+
/// Main-menu style: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=2037-186672&m=dev
125+
/// Other style: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=6025-293468&m=dev
126+
quantity,
127+
}
128+
101129
class MutedUnreadBadge extends StatelessWidget {
102130
const MutedUnreadBadge({super.key});
103131

lib/widgets/home.dart

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import 'store.dart';
2424
import 'subscription_list.dart';
2525
import 'text.dart';
2626
import 'theme.dart';
27-
import 'unread_count_badge.dart';
27+
import 'counter_badge.dart';
2828
import 'user.dart';
2929

3030
enum _HomePageTab {
@@ -635,8 +635,9 @@ class _InboxButton extends _NavigationBarMenuButton {
635635
final store = PerAccountStoreWidget.of(context);
636636
final unreadCount = store.unreads.countInCombinedFeedNarrow();
637637
if (unreadCount == 0) return null;
638-
return UnreadCountBadge(
639-
style: UnreadCountBadgeStyle.mainMenu,
638+
return CounterBadge(
639+
kind: CounterBadgeKind.unread,
640+
style: CounterBadgeStyle.mainMenu,
640641
count: unreadCount,
641642
channelIdForBackground: null,
642643
);
@@ -662,8 +663,9 @@ class _MentionsButton extends MenuButton {
662663
final store = PerAccountStoreWidget.of(context);
663664
final unreadCount = store.unreads.countInMentionsNarrow();
664665
if (unreadCount == 0) return null;
665-
return UnreadCountBadge(
666-
style: UnreadCountBadgeStyle.mainMenu,
666+
return CounterBadge(
667+
kind: CounterBadgeKind.unread,
668+
style: CounterBadgeStyle.mainMenu,
667669
count: unreadCount,
668670
channelIdForBackground: null,
669671
);
@@ -687,6 +689,18 @@ class _StarredMessagesButton extends MenuButton {
687689
return zulipLocalizations.starredMessagesPageTitle;
688690
}
689691

692+
@override
693+
Widget? buildTrailing(BuildContext context) {
694+
final store = PerAccountStoreWidget.of(context);
695+
if (!store.userSettings.starredMessageCounts) return null;
696+
return CounterBadge(
697+
kind: CounterBadgeKind.quantity,
698+
style: CounterBadgeStyle.mainMenu,
699+
count: store.starredMessages.length,
700+
channelIdForBackground: null,
701+
);
702+
}
703+
690704
@override
691705
void onPressed(BuildContext context) {
692706
Navigator.of(context).push(MessageListPage.buildRoute(
@@ -743,8 +757,9 @@ class _DirectMessagesButton extends _NavigationBarMenuButton {
743757
final store = PerAccountStoreWidget.of(context);
744758
final unreadCount = store.unreads.countInDms();
745759
if (unreadCount == 0) return null;
746-
return UnreadCountBadge(
747-
style: UnreadCountBadgeStyle.mainMenu,
760+
return CounterBadge(
761+
kind: CounterBadgeKind.unread,
762+
style: CounterBadgeStyle.mainMenu,
748763
count: unreadCount,
749764
channelIdForBackground: null,
750765
);

lib/widgets/inbox.dart

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import 'sticky_header.dart';
1313
import 'store.dart';
1414
import 'text.dart';
1515
import 'theme.dart';
16-
import 'unread_count_badge.dart';
16+
import 'counter_badge.dart';
1717

1818
class InboxPageBody extends StatefulWidget {
1919
const InboxPageBody({super.key});
@@ -309,7 +309,9 @@ abstract class _HeaderItem extends StatelessWidget {
309309
const SizedBox(width: 12),
310310
if (hasMention) const _IconMarker(icon: ZulipIcons.at_sign),
311311
Padding(padding: const EdgeInsetsDirectional.only(end: 16),
312-
child: UnreadCountBadge(
312+
child: CounterBadge(
313+
// TODO(design) use CounterKind.quantity, following Figma
314+
kind: CounterBadgeKind.unread,
313315
channelIdForBackground: channelId,
314316
count: count)),
315317
])));
@@ -431,7 +433,10 @@ class _DmItem extends StatelessWidget {
431433
const SizedBox(width: 12),
432434
if (hasMention) const _IconMarker(icon: ZulipIcons.at_sign),
433435
Padding(padding: const EdgeInsetsDirectional.only(end: 16),
434-
child: UnreadCountBadge(channelIdForBackground: null,
436+
child: CounterBadge(
437+
// TODO(design) use CounterKind.quantity, following Figma
438+
kind: CounterBadgeKind.unread,
439+
channelIdForBackground: null,
435440
count: count)),
436441
]))));
437442
}
@@ -565,7 +570,9 @@ class _TopicItem extends StatelessWidget {
565570
// TODO(design) copies the "@" marker color; is there a better color?
566571
if (visibilityIcon != null) _IconMarker(icon: visibilityIcon),
567572
Padding(padding: const EdgeInsetsDirectional.only(end: 16),
568-
child: UnreadCountBadge(
573+
child: CounterBadge(
574+
// TODO(design) use CounterKind.quantity, following Figma
575+
kind: CounterBadgeKind.unread,
569576
channelIdForBackground: streamId,
570577
count: count)),
571578
]))));

lib/widgets/recent_dm_conversations.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import 'page.dart';
1111
import 'store.dart';
1212
import 'text.dart';
1313
import 'theme.dart';
14-
import 'unread_count_badge.dart';
14+
import 'counter_badge.dart';
1515
import 'user.dart';
1616

1717
typedef OnDmSelectCallback = void Function(DmNarrow narrow);
@@ -234,7 +234,9 @@ class RecentDmConversationsItem extends StatelessWidget {
234234
const SizedBox(width: 12),
235235
unreadCount > 0
236236
? Padding(padding: const EdgeInsetsDirectional.only(end: 16),
237-
child: UnreadCountBadge(channelIdForBackground: null,
237+
child: CounterBadge(
238+
kind: CounterBadgeKind.unread,
239+
channelIdForBackground: null,
238240
count: unreadCount))
239241
: const SizedBox(),
240242
]))));

lib/widgets/subscription_list.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import 'page.dart';
1414
import 'store.dart';
1515
import 'text.dart';
1616
import 'theme.dart';
17-
import 'unread_count_badge.dart';
17+
import 'counter_badge.dart';
1818

1919
typedef OnChannelSelectCallback = void Function(ChannelNarrow narrow);
2020

@@ -336,7 +336,8 @@ class SubscriptionItem extends StatelessWidget {
336336
// TODO(#747) show @-mention indicator when it applies
337337
Opacity(
338338
opacity: opacity,
339-
child: UnreadCountBadge(
339+
child: CounterBadge(
340+
kind: CounterBadgeKind.unread,
340341
count: unreadCount,
341342
channelIdForBackground: subscription.streamId)),
342343
] else if (showMutedUnreadBadge) ...[

lib/widgets/theme.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
184184
foreground: const Color(0xff000000),
185185
icon: const Color(0xff6159e1),
186186
iconSelected: const Color(0xff222222),
187+
labelCounterQuantity: const Color(0xff222222).withValues(alpha: 0.6),
187188
labelCounterUnread: const Color(0xff1a1a1a),
188189
labelEdited: const HSLColor.fromAHSL(0.35, 0, 0, 0).toColor(),
189190
labelMenuButton: const Color(0xff222222),
@@ -285,6 +286,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
285286
foreground: const Color(0xffffffff),
286287
icon: const Color(0xff7977fe),
287288
iconSelected: Colors.white.withValues(alpha: 0.8),
289+
labelCounterQuantity: const Color(0xffffffff).withValues(alpha: 0.7),
288290
labelCounterUnread: const Color(0xffffffff).withValues(alpha: 0.95),
289291
labelEdited: const HSLColor.fromAHSL(0.35, 0, 0, 1).toColor(),
290292
labelMenuButton: const Color(0xffffffff).withValues(alpha: 0.85),
@@ -395,6 +397,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
395397
required this.fabShadow,
396398
required this.icon,
397399
required this.iconSelected,
400+
required this.labelCounterQuantity,
398401
required this.labelCounterUnread,
399402
required this.labelEdited,
400403
required this.labelMenuButton,
@@ -496,6 +499,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
496499
final Color foreground;
497500
final Color icon;
498501
final Color iconSelected;
502+
final Color labelCounterQuantity;
499503
final Color labelCounterUnread;
500504
final Color labelEdited;
501505
final Color labelMenuButton;
@@ -592,6 +596,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
592596
Color? foreground,
593597
Color? icon,
594598
Color? iconSelected,
599+
Color? labelCounterQuantity,
595600
Color? labelCounterUnread,
596601
Color? labelEdited,
597602
Color? labelMenuButton,
@@ -683,6 +688,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
683688
fabShadow: fabShadow ?? this.fabShadow,
684689
icon: icon ?? this.icon,
685690
iconSelected: iconSelected ?? this.iconSelected,
691+
labelCounterQuantity: labelCounterQuantity ?? this.labelCounterQuantity,
686692
labelCounterUnread: labelCounterUnread ?? this.labelCounterUnread,
687693
labelEdited: labelEdited ?? this.labelEdited,
688694
labelMenuButton: labelMenuButton ?? this.labelMenuButton,
@@ -781,6 +787,7 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
781787
fabShadow: Color.lerp(fabShadow, other.fabShadow, t)!,
782788
icon: Color.lerp(icon, other.icon, t)!,
783789
iconSelected: Color.lerp(iconSelected, other.iconSelected, t)!,
790+
labelCounterQuantity: Color.lerp(labelCounterQuantity, other.labelCounterQuantity, t)!,
784791
labelCounterUnread: Color.lerp(labelCounterUnread, other.labelCounterUnread, t)!,
785792
labelEdited: Color.lerp(labelEdited, other.labelEdited, t)!,
786793
labelMenuButton: Color.lerp(labelMenuButton, other.labelMenuButton, t)!,

lib/widgets/topic_list.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import 'page.dart';
1414
import 'store.dart';
1515
import 'text.dart';
1616
import 'theme.dart';
17-
import 'unread_count_badge.dart';
17+
import 'counter_badge.dart';
1818

1919
class TopicListPage extends StatelessWidget {
2020
const TopicListPage({super.key, required this.streamId});
@@ -307,7 +307,8 @@ class _TopicItem extends StatelessWidget {
307307
if (hasMention) const _IconMarker(icon: ZulipIcons.at_sign),
308308
if (visibilityIcon != null) _IconMarker(icon: visibilityIcon),
309309
if (unreadCount > 0)
310-
UnreadCountBadge(
310+
CounterBadge(
311+
kind: CounterBadgeKind.unread,
311312
count: unreadCount,
312313
channelIdForBackground: null),
313314
])),

test/widgets/checks.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import 'package:zulip/widgets/message_list.dart';
1717
import 'package:zulip/widgets/page.dart';
1818
import 'package:zulip/widgets/profile.dart';
1919
import 'package:zulip/widgets/store.dart';
20-
import 'package:zulip/widgets/unread_count_badge.dart';
20+
import 'package:zulip/widgets/counter_badge.dart';
2121
import 'package:zulip/widgets/user.dart';
2222

2323
extension ChannelColorSwatchChecks on Subject<ChannelColorSwatch> {
@@ -92,7 +92,7 @@ extension PerAccountStoreWidgetChecks on Subject<PerAccountStoreWidget> {
9292
Subject<Widget> get child => has((x) => x.child, 'child');
9393
}
9494

95-
extension UnreadCountBadgeChecks on Subject<UnreadCountBadge> {
95+
extension UnreadCountBadgeChecks on Subject<CounterBadge> {
9696
Subject<int> get count => has((b) => b.count, 'count');
9797
Subject<int?> get channelIdForBackground => has((b) => b.channelIdForBackground, 'channelIdForBackground');
9898
}

0 commit comments

Comments
 (0)