85 lines
2.3 KiB
Dart
85 lines
2.3 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../di/injector.dart';
|
|
import '../services/unread_service.dart';
|
|
|
|
/// Wraps [child] with a small live unread-count badge. It listens to the
|
|
/// app-wide [UnreadService] and rebuilds whenever a count changes: on a new
|
|
/// message, or when a chat is opened (marked read). Shows the total across all
|
|
/// conversations, or — when [peer] is set — just that peer's count.
|
|
///
|
|
/// When no unread tracker is available (widget tests, inventory-only builds) or
|
|
/// the count is zero, it renders [child] unadorned. Positioning is via Flutter's
|
|
/// [Badge], which is directional (top-`end`), so it's correct under RTL.
|
|
class UnreadBadge extends StatefulWidget {
|
|
const UnreadBadge({
|
|
required this.child,
|
|
this.peer,
|
|
this.service,
|
|
super.key,
|
|
});
|
|
|
|
final Widget child;
|
|
|
|
/// A specific conversation's count; null means the app-wide total.
|
|
final String? peer;
|
|
|
|
/// Overrides the [UnreadService] lookup (for tests). Falls back to `getIt`.
|
|
final UnreadService? service;
|
|
|
|
@override
|
|
State<UnreadBadge> createState() => _UnreadBadgeState();
|
|
}
|
|
|
|
class _UnreadBadgeState extends State<UnreadBadge> {
|
|
UnreadService? _service;
|
|
StreamSubscription<void>? _sub;
|
|
int _count = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_service = widget.service ??
|
|
(getIt.isRegistered<UnreadService>() ? getIt<UnreadService>() : null);
|
|
final service = _service;
|
|
if (service != null) {
|
|
_refresh();
|
|
_sub = service.changes.listen((_) => _refresh());
|
|
}
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(UnreadBadge old) {
|
|
super.didUpdateWidget(old);
|
|
// A recycled row may now point at a different peer.
|
|
if (old.peer != widget.peer) _refresh();
|
|
}
|
|
|
|
Future<void> _refresh() async {
|
|
final service = _service;
|
|
if (service == null) return;
|
|
final peer = widget.peer;
|
|
final count = peer == null
|
|
? await service.totalUnreadCount()
|
|
: await service.unreadCount(peer);
|
|
if (mounted) setState(() => _count = count);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_sub?.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Badge(
|
|
isLabelVisible: _count > 0,
|
|
label: Text('$_count'),
|
|
backgroundColor: Theme.of(context).colorScheme.error,
|
|
child: widget.child,
|
|
);
|
|
}
|
|
}
|