show badges totaled in more areas

This commit is contained in:
Henry Hiles 2026-10-15 00:42:56 -04:00
commit 66a437094a
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
6 changed files with 79 additions and 22 deletions

View file

@ -0,0 +1,16 @@
import "package:material_ui/material_ui.dart";
/// Shows [count] in a badge on [child]: nothing if null, a dot if 0, and the
/// number otherwise.
class const CountBadge(
final int? count, {
required final Widget child,
super.key,
}) extends StatelessWidget {
@override
Widget build(BuildContext context) => switch (count) {
null => child,
0 => Badge(child: child),
final count => Badge.count(count: count, child: child),
};
}

View file

@ -3,8 +3,10 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/count_badge.dart";
import "package:nexus/widgets/room_menu_button.dart";
import "package:nexus/widgets/room_summary_dialog.dart";
import "package:nexus/widgets/sidebar.dart";
final class const RoomAppbar({
required final String? roomId,
@ -49,7 +51,14 @@ final class const RoomAppbar({
dimension: 24,
fallback: Icon(Icons.numbers),
)
: DrawerButton(onPressed: onOpenDrawer),
: IconButton(
onPressed: onOpenDrawer,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
icon: CountBadge(
Sidebar.totalBadgeCount(ref),
child: const DrawerButtonIcon(),
),
),
scrolledUnderElevation: 0,
title: room == null
? null

View file

@ -13,6 +13,7 @@ import "package:nexus/widgets/pages/settings.dart";
import "package:nexus/widgets/join_dialog.dart";
import "package:nexus/widgets/room_menu_button.dart";
import "package:nexus/controllers/key.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/controllers/spaces.dart";
import "package:collection/collection.dart";
@ -26,9 +27,26 @@ class const RoomList({super.key}) extends ConsumerWidget implements NavPage {
@override
IconData get icon => Icons.list;
// TODO: Show unread badge here and on sidebar hamburger
@override
int? badgeCount(WidgetRef ref) => null;
int? badgeCount(WidgetRef ref) {
final (notifications, hasUnread) = ref.watch(
RoomsController.provider.select(
(rooms) => rooms.values.fold(
(0, false),
(total, room) => (
total.$1 + (room.metadata?.unreadNotifications ?? 0),
total.$2 || (room.metadata?.unreadMessages ?? 0) > 0,
),
),
),
);
return notifications > 0
? notifications
: hasUnread
? 0
: null;
}
@override
Widget build(BuildContext context, WidgetRef ref) {

View file

@ -1,23 +1,36 @@
import "package:collection/collection.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/controllers/sidebar_page.dart";
import "package:nexus/models/nav_page.dart";
import "package:nexus/models/sidebar_page.dart";
import "package:nexus/widgets/count_badge.dart";
import "package:nexus/widgets/invites_list.dart";
import "package:nexus/widgets/notifications_list.dart";
import "package:nexus/widgets/room_list.dart";
class const Sidebar({super.key}) extends ConsumerWidget {
static NavPage pageFor(SidebarPage page) => switch (page) {
.rooms => const RoomList(),
.notifications => const NotificationsList(),
.invites => const InvitesList(),
};
/// The badges of every tab added together, e.g. for the drawer button.
///
/// Like [NavPage.badgeCount], null means no badges and 0 means only dots.
static int? totalBadgeCount(WidgetRef ref) {
final counts = SidebarPage.values
.map((page) => pageFor(page).badgeCount(ref))
.nonNulls;
return counts.isEmpty ? null : counts.sum;
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final selected = ref.watch(SidebarPageController.provider);
final pages = SidebarPage.values.map<NavPage>(
(page) => switch (page) {
.rooms => const RoomList(),
.notifications => const NotificationsList(),
.invites => const InvitesList(),
},
);
final pages = SidebarPage.values.map(pageFor);
return Drawer(
width: 330,
@ -27,18 +40,17 @@ class const Sidebar({super.key}) extends ConsumerWidget {
body: pages.elementAt(selected.index),
bottomNavigationBar: NavigationBar(
selectedIndex: selected.index,
destinations: pages.map((page) {
final badgeCount = page.badgeCount(ref);
return NavigationDestination(
icon: Badge.count(
count: badgeCount ?? 0,
isLabelVisible: badgeCount != null,
child: Icon(page.icon),
),
label: page.title,
);
}).toList(),
destinations: pages
.map(
(page) => NavigationDestination(
icon: CountBadge(
page.badgeCount(ref),
child: Icon(page.icon),
),
label: page.title,
),
)
.toList(),
onDestinationSelected: (index) => ref
.read(SidebarPageController.provider.notifier)
.set(SidebarPage.values[index]),